C# TFS API:显示带有文件夹和文件的项目结构,包括它们的 ChangeType(签出、删除、重命名),就像在 Visual Studio 中一样

编程入门 行业动态 更新时间:2024-10-09 19:14:16
本文介绍了C# TFS API:显示带有文件夹和文件的项目结构,包括它们的 ChangeType(签出、删除、重命名),就像在 Visual Studio 中一样的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

限时送ChatGPT账号..

我需要创建 GUI 来显示我的 TFS 项目的所有可用文件夹和文件结构,对于特定文件夹.例如:我在屏幕截图上有DiagnosticsFolder":

I need to create GUI that shows all available folders and files structure for my TFS project, for particular folder. For example: I have "DiagnosticsFolder" like on screenshot:

并且我需要在所需文件夹下显示具有项目结构的Tree,包括这些文件和文件夹ChangeType(例如:编辑、由其他用户编辑、添加、删除等).

And I need to show Tree with project structure under required folder, including these files and folders ChangeType (for instance: edited, edited by another user, added, deleted etcetera).

我找到了很多部分解决方案,提供使用一些方法,但我还没有找到完整的解决方案,而且确定文件和文件夹状态(ChangeType)也相当具有挑战性.

I found a lot of partial solutions, offereing to use some methods, however I haven't found full solution, and it is fairly challenging to determine files and folders status (ChangeType ) too.

需要类似的东西:

推荐答案

我自己已经完成了解决方案.它包括以下部分:1).查看代表源代码控制项的模型:

I've done solution myself. It includes following parts: 1). View Models that represents Source Control Item:

 public abstract class SourceControlItemViewBaseModel : ViewModelBase
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    private string _localPath;
    public string LocalPath
    {
        get { return _localPath; }
        set
        {
            _localPath = value;
            OnPropertyChanged();
        }
    }

    private string _serverPath;
    public string ServerPath
    {
        get { return _serverPath; }
        set
        {
            _serverPath = value;
            OnPropertyChanged();
        }
    }

    private string _pendingSetName;
    /// <summary>
    /// Computer name where changes were made
    /// </summary>
    public string PendingSetName
    {
        get { return _pendingSetName; }
        set
        {
            _pendingSetName = value;
            OnPropertyChanged();
        }
    }

    private string _pendingSetOwner;
    /// <summary>
    /// User Name who made the changes
    /// </summary>
    public string PendingSetOwner
    {
        get { return _pendingSetOwner; }
        set
        {
            _pendingSetOwner = value;
            OnPropertyChanged();
        }
    }

    private string _toolTipText;
    public string ToolTipText
    {
        get { return _toolTipText; }
        set
        {
            _toolTipText = value;
            OnPropertyChanged();
        }
    }

    public string SourceServerItem
    {
        get { return _sourceServerItem; }
        set
        {
            _sourceServerItem = value;
            OnPropertyChanged();
        }
    }

    private SourceControlState _state;
    private string _sourceServerItem;

    public SourceControlState State
    {
        get { return _state; }
        set
        {
            _state = value;
            OnPropertyChanged();
        }
    }
}

public class SourceControlFileViewModel : SourceControlItemViewBaseModel
{
}

 public class SourceControlDirecoryViewModel : SourceControlItemViewBaseModel
{
    public List<SourceControlItemViewBaseModel> Items { get; set; }

    public SourceControlDirecoryViewModel()
    {
        Items = new List<SourceControlItemViewBaseModel>();
    }
}



[Flags]//My own Enum that represents different states
public enum SourceControlState
{
    Online = 0,
    CheckedOut = 1,
    Added = 2,
    Deleted = 4,
    Locked = 8,
    Renamed = 16
}

2).使用递归方法构建结构树的存储库:

2). Repository with recursive method that build the structure tree:

 /// <summary>
    /// 
    /// </summary>
    /// <param name="serverPath">TFS source path</param>
    /// <param name="serverSourcePath">this path should be used in the case server item's name was changed. Needs to be used because TFS cannot get items from folder whose name has been changed</param>
    /// <returns></returns>
    public List<SourceControlItemViewBaseModel> BuildSourceControlStructure(string serverPath = ConstDefaultFlowsTfsPath, string serverSourcePath = null)
    {
        #region Local members
        var resultItems = new List<SourceControlItemViewBaseModel>();
        var workspaceInfo = Workstation.Current.GetLocalWorkspaceInfo(serverPath);
        var server = RegisteredTfsConnections.GetProjectCollection(workspaceInfo.ServerUri);
        var projects = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(server);
        var versionControl = (VersionControlServer)projects.GetService(typeof(VersionControlServer));
        var workspace = versionControl.GetWorkspace(Environment.MachineName, Environment.UserName);
        #endregion

        #region Extraction of folders information and pending changes
        //Info from Directories
        PendingChange[] changesFoldersByAnotherUser =
            versionControl.QueryPendingSets(new[] { serverPath }, RecursionType.OneLevel, null, null)//this method brings changes that were made by another user or through another machine, whereas workspace.GetPendingChanges() method doesn't.
                .SelectMany(pnd => pnd.PendingChanges)
                .Where(i => i.ItemType == ItemType.Folder && i.ServerItem != serverPath &&
                (i.PendingSetOwner != WindowsIdentity.GetCurrent()?.Name || i.PendingSetName != Environment.MachineName))
                .ToArray();

        PendingChange[] changesFolders = workspace.GetPendingChanges(serverPath, RecursionType.OneLevel)//needs to show changes under renamed folders
                .Where(i => i.ItemType == ItemType.Folder && i.ServerItem != serverPath)
                .ToArray();

        Item[] itemsFolders = versionControl.GetItems(serverPath, RecursionType.OneLevel).Items
         .Where(item => item.ItemType == ItemType.Folder && item.ServerItem != serverPath &&//needs to avoid duplicate presentation of folders
            changesFoldersByAnotherUser.All(chng => chng.ServerItem != item.ServerItem && chng.SourceServerItem != item.ServerItem) &&//needs to avoid duplicate presentation of folders
                changesFolders.All(chg => chg.ServerItem != item.ServerItem && chg.SourceServerItem != item.ServerItem)).ToArray();

        if (serverSourcePath != null)//means folder name has been changed, therefore items cannot be got through using serverPath
        {
            itemsFolders = versionControl.GetItems(serverSourcePath, RecursionType.OneLevel).Items
             .Where(item => item.ItemType == ItemType.Folder && item.ServerItem != serverSourcePath && changesFolders
                .All(chng => chng.ServerItem != item.ServerItem && chng.SourceServerItem != item.ServerItem)).ToArray();
        }
        #endregion

        #region Initialization of Items and sub items for folder
        //Folder item with changing
        foreach (Item folderItem in itemsFolders)
        {
            var vm = new SourceControlDirecoryViewModel
            {
                Name = Path.GetFileName(folderItem.ServerItem),
                LocalPath = workspace?.GetLocalItemForServerItem(folderItem.ServerItem),
                ServerPath = folderItem.ServerItem,
                Items = BuildSourceControlStructure(folderItem.ServerItem),
                PendingSetName = null,
                PendingSetOwner = null,
                SourceServerItem = null,
                ToolTipText = "Connected to TFS",
                State = SourceControlState.Online

            };
            resultItems.Add(vm);
        }

        foreach (PendingChange currentFolderChange in changesFolders)
        {
            var vm = new SourceControlDirecoryViewModel
            {
                Name = Path.GetFileName(currentFolderChange.ServerItem),
                LocalPath = workspace?.GetLocalItemForServerItem(currentFolderChange.ServerItem),
                ServerPath = currentFolderChange.ServerItem,
                Items = BuildSourceControlStructure(currentFolderChange.ServerItem, currentFolderChange.SourceServerItem),
                PendingSetName = currentFolderChange?.PendingSetName,
                PendingSetOwner = currentFolderChange?.PendingSetOwner,
                SourceServerItem = currentFolderChange?.SourceServerItem,
                ToolTipText = currentFolderChange?.ToolTipText,
                State = ConvertControlState(currentFolderChange)

            };
            resultItems.Add(vm);
        }

        foreach (PendingChange folderChangeByAnUser in changesFoldersByAnotherUser)
        {
            var vm = new SourceControlDirecoryViewModel
            {
                Name = Path.GetFileName(folderChangeByAnUser.ServerItem),
                LocalPath = workspace?.GetLocalItemForServerItem(folderChangeByAnUser.ServerItem),
                ServerPath = folderChangeByAnUser.ServerItem,
                Items = BuildSourceControlStructure(folderChangeByAnUser.ServerItem),
                PendingSetName = folderChangeByAnUser?.PendingSetName,
                PendingSetOwner = folderChangeByAnUser?.PendingSetOwner,
                SourceServerItem = folderChangeByAnUser?.SourceServerItem,
                ToolTipText = folderChangeByAnUser?.ToolTipText,
                State = ConvertControlState(folderChangeByAnUser)

            };
            resultItems.Add(vm);
        }
        #endregion

        #region Extraction of files information and pending changes
        PendingChange[] changesFilesByAnotherUser =
       versionControl.QueryPendingSets(new[] { serverPath }, RecursionType.OneLevel, null, null)//this method brings changes that were made by another user or through another machine, whereas workspace.GetPendingChanges() method doesn't.
           .SelectMany(pnd => pnd.PendingChanges)
           .Where(i => i.ItemType == ItemType.File && i.ServerItem != serverPath &&
              (i.PendingSetOwner != WindowsIdentity.GetCurrent()?.Name || i.PendingSetName != Environment.MachineName)
              && i.ServerItem.EndsWith(ConstTargetFileExtenstion))//filter files by extension
              .ToArray();


        PendingChange[] changesFiles = workspace.GetPendingChanges(serverPath, RecursionType.OneLevel)
             .Where(i => i.ItemType == ItemType.File && i.ServerItem != serverPath)
             .ToArray();


        Item[] itemsFiles = versionControl.GetItems(serverPath, RecursionType.OneLevel).Items
            .Where(item => item.ItemType == ItemType.File && item.ServerItem != serverPath && item.ServerItem.EndsWith(ConstTargetFileExtenstion) &&//filter files by extension
                changesFilesByAnotherUser.All(chng => chng.ServerItem != item.ServerItem && chng.SourceServerItem != item.ServerItem) &&//needs to avoid duplicate presentation of folders
                    changesFiles.All(chg => chg.ServerItem != item.ServerItem && chg.SourceServerItem != item.ServerItem)).ToArray();//needs to avoid duplicate presentation of folders

        if (serverSourcePath != null)//needs to use serverSourcePath(full server path before it was changed) to receive its content
        {
            itemsFiles = versionControl.GetItems(serverSourcePath, RecursionType.OneLevel).Items
                .Where(item => item.ItemType == ItemType.File && item.ServerItem != serverPath && item.ServerItem.EndsWith(ConstTargetFileExtenstion)//filter files by extension
                && changesFiles.All(chng => chng.ServerItem != item.ServerItem && chng.SourceServerItem != item.ServerItem))
                .ToArray();
        }
        #endregion

        #region Ininialization of Items and sub items for files
        foreach (Item fileItem in itemsFiles)
        {
            var vm = new SourceControlFileViewModel
            {
                Name = Path.GetFileName(fileItem.ServerItem),
                ServerPath = fileItem.ServerItem,
                LocalPath = workspace?.GetLocalItemForServerItem(fileItem.ServerItem),
                PendingSetName = null,
                PendingSetOwner = null,
                SourceServerItem = null,
                ToolTipText = "Connected to TFS",
                State = SourceControlState.Online
            };
            resultItems.Add(vm);
        }

        foreach (PendingChange currentFilePendChange in changesFiles)
        {
            var vm = new SourceControlFileViewModel
            {
                Name = Path.GetFileName(currentFilePendChange.ServerItem),
                ServerPath = currentFilePendChange.ServerItem,
                LocalPath = workspace?.GetLocalItemForServerItem(currentFilePendChange.ServerItem),
                PendingSetName = currentFilePendChange?.PendingSetName,
                PendingSetOwner = currentFilePendChange?.PendingSetOwner,
                SourceServerItem = currentFilePendChange?.SourceServerItem,
                ToolTipText = currentFilePendChange?.ToolTipText,
                State = ConvertControlState(currentFilePendChange)
            };
            resultItems.Add(vm);
        }

        foreach (PendingChange fileChangeByAnUser in changesFilesByAnotherUser)
        {
            SourceControlState state = ConvertControlState(fileChangeByAnUser);
            string localPath = workspace?.GetLocalItemForServerItem(state == (SourceControlState.Locked | SourceControlState.Renamed) ? fileChangeByAnUser?.SourceServerItem : fileChangeByAnUser.ServerItem);

            var vm = new SourceControlFileViewModel
            {
                Name = Path.GetFileName(localPath),
                ServerPath = fileChangeByAnUser?.ServerItem,
                LocalPath = localPath,
                PendingSetName = fileChangeByAnUser?.PendingSetName,
                PendingSetOwner = fileChangeByAnUser?.PendingSetOwner,
                SourceServerItem = fileChangeByAnUser?.SourceServerItem,
                ToolTipText = fileChangeByAnUser?.ToolTipText,
                State = state
            };
            resultItems.Add(vm);
        }
        #endregion

        return resultItems;
    }

我让它变得如此复杂,因为我必须使用不同的方法来提取文件的信息,而无需更改、更改以及其他用户所做的更改.允许异步使用的方法:公共任务> BuildSourceControlStructureAsync(string folderPath = ConstDefaultFlowsTfsPath){返回 Task.Run(() => BuildSourceControlStructure(folderPath));}

I've made it so complex because I had to use different method to extract information for files without changes, with changes and with changes that were made by another user. The method that allows async usage: public Task> BuildSourceControlStructureAsync(string folderPath = ConstDefaultFlowsTfsPath) { return Task.Run(() => BuildSourceControlStructure(folderPath)); }

3).源代码管理视图模型:

3). Source Control View model:

 public class SourceControlViewModel : ViewModelBase
    {

 public IEnumerable<SourceControlItemViewBaseModel> SourceControlStructureItems { get; set; }
    public async Task Init()
    {
        await SourceControlRepository.Instance.Init();
        //Here the strucure builds
        SourceControlStructureItems = await SourceControlRepository.Instance.BuildSourceControlStructureAsync();

    }
}

4).XAML:

  <TreeView ItemsSource="{Binding SourceControlStructureItems}" />

5).资源(TreeView 的数据模板):

5). Resources (datatemplates for TreeView):

 <HierarchicalDataTemplate DataType="{x:Type viewModels:SourceControlDirecoryViewModel}"
                              ItemsSource="{Binding Items}">
        <Grid x:Name="LayoutRoot">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="5" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="5" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Image Width="9"
                   Height="9"
                   Grid.Column="0"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   ToolTipService.ShowOnDisabled="True"
                   x:Name="srcCtrlStatusIndicator">
                <FrameworkElement.ToolTip>
                    <ToolTip>
                        <TextBlock Text="{Binding State}" />
                    </ToolTip>
                </FrameworkElement.ToolTip>
            </Image>
            <Image Width="16"
                   Height="16"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   Source="{StaticResource ImageSourceFolderClosed16x16}"
                   x:Name="img"
                   Grid.Column="2" />
            <TextBlock Text="{Binding Path=Name}"
                       ToolTipService.ShowOnDisabled="True"
                       VerticalAlignment="Center"
                       Grid.Column="4"
                       x:Name="txt">
                <FrameworkElement.ToolTip>
                    <ToolTip>
                        <TextBlock Text="{Binding Path=LocalPath}"
                                   x:Name="txtToolTip" />
                    </ToolTip>
                </FrameworkElement.ToolTip>
            </TextBlock>
        </Grid>

        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding IsExpanded, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type TreeViewItem}}}"
                         Value="True">
                <Setter Property="Source"
                        TargetName="img"
                        Value="{StaticResource ImageSourceFolderOpened16x16}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding State}"
                         Value="Deleted">
                <Setter Property="TextBlock.TextDecorations"
                        TargetName="txt"
                        Value="Strikethrough" />
            </DataTrigger>
            <!--<DataTrigger Binding="{Binding State}"
                         Value="CheckedOut">
                <Setter Property="Source"
                        TargetName="srcCtrlStatusIndicator"
                        Value="{StaticResource ImageSourceFolderCheckedOut9x9}" />
            </DataTrigger>-->
            <DataTrigger Binding="{Binding State}"
                         Value="Added">
                <Setter Property="Source"
                        TargetName="srcCtrlStatusIndicator"
                        Value="{StaticResource ImageSourceFolderAdded9x9}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding State}"
                         Value="Deleted">
                <Setter Property="Source"
                        TargetName="srcCtrlStatusIndicator"
                        Value="{StaticResource ImageSourceFolderRemove16x16}" />
                <Setter Property="TextBlock.TextDecorations"
                        TargetName="txt"
                        Value="Strikethrough" />
            </DataTrigger>
            <DataTrigger Binding="{Binding State}"
                         Value="Renamed">
                <Setter Property="Source"
                        TargetName="srcCtrlStatusIndicator"
                        Value="{StaticResource ImageSourceRenamed9x9}" />
                <Setter Property="Text"
                        TargetName="txt">
                    <Setter.Value>
                        <MultiBinding StringFormat="{}{0} [{1}]">
                            <Binding Path="ServerPath"
                                     Converter="{cnv:FullPathToShortNameConverter}"
                                     Mode="OneWay" />
                            <Binding Path="SourceServerItem"
                                     Converter="{cnv:FullPathToShortNameConverter}"
                                     Mode="OneWay" />
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
                <Setter Property="Text"
                        TargetName="txtToolTip">
                    <Setter.Value>
                        <MultiBinding StringFormat="{}[{1}] &#x0a; was renamed to &#x0a; {0} ">
                            <Binding Path="ServerPath"
                                     Mode="OneWay" />
                            <Binding Path="SourceServerItem"
                                     Mode="OneWay" />
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
            <DataTrigger Binding="{Binding State}"
                         Value="Locked">
                <Setter Property="Source"
                        TargetName="srcCtrlStatusIndicator"
                        Value="{StaticResource ImageSourceCheckedOutBySomeoneElse9x9}" />
                <Setter Property="Opacity"
                        TargetName="txt"
                        Value="0.5" />
                <Setter Property="Text"
                        TargetName="txtToolTip">
                    <Setter.Value>
                        <MultiBinding StringFormat="{} Folder is readonly and uneditable. &#x0a; Reason: it has been checked out by: [{1}] &#x0a; On machine: {0}  ">
                            <Binding Path="PendingSetName"
                                     Mode="OneWay" />
                            <Binding Path="PendingSetOwner"
                                     Mode="OneWay" />
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </DataTemplate.Triggers>
    </HierarchicalDataTemplate>

    <DataTemplate DataType="{x:Type viewModels:SourceControlFileViewModel}">
        <Grid x:Name="LayoutRoot">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="5" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="5" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Image Width="9"
                   Height="9"
                   ToolTipService.ShowOnDisabled="True"
                   VerticalAlignment="Center"
                   HorizontalAlignment="Center"
                   Source="{StaticResource ImageSourceFolderUnderSourceControl9x9}"
                   x:Name="srcCtrlStatusIndicator">
                <FrameworkElement.ToolTip>
                    <ToolTip>
                        <TextBlock Text="{Binding State}" />
                    </ToolTip>
                </FrameworkElement.ToolTip>

            </Image>
            <Image Width="16"
                   Height="16"
                   Grid.Column="2"
                   VerticalAlignment="Center"
                   Source="{StaticResource ImageSourceFolderXaml16x16}" />
            <TextBlock Text="{Binding Path=Name}"
                       ToolTipService.ShowOnDisabled="True"
                       VerticalAlignment="Center"
                       Grid.Column="4"
                       x:Name="txt">
                <FrameworkElement.ToolTip>
                    <ToolTip>
                        <TextBlock Text="{Binding Path=LocalPath}"
                                   x:Name="txtToolTip" />
                    </ToolTip>
                </FrameworkElement.ToolTip>
            </TextBlock>
        </Grid>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding State}"
                         Value="CheckedOut">
                <Setter Property="Source"
                        TargetName="srcCtrlStatusIndicator"
                        Value="{StaticResource ImageSourceFolderCheckedOut9x9}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding State}"
                         Value="Added">
                <Setter Property="Source"
                        TargetName="srcCtrlStatusIndicator"
                        Value="{StaticResource ImageSourceFolderAdded9x9}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding State}"
                         Value="Deleted">
                <Setter Property="Source"
                        TargetName="srcCtrlStatusIndicator"
                        Value="{StaticResource ImageSourceFolderRemove16x16}" />
                <Setter Property="TextBlock.TextDecorations"
                        TargetName="txt"
                        Value="Strikethrough" />
            </DataTrigger>
            <DataTrigger Binding="{Binding State}"
                         Value="Renamed">
                <Setter Property="Source"
                        TargetName="srcCtrlStatusIndicator"
                        Value="{StaticResource ImageSourceRenamed9x9}" />
                <Setter Property="Text"
                        TargetName="txt">
                    <Setter.Value>
                        <MultiBinding StringFormat="{}{0} [{1}]">
                            <Binding Path="ServerPath"
                                     Converter="{cnv:FullPathToShortNameConverter}"
                                     Mode="OneWay" />
                            <Binding Path="SourceServerItem"
                                     Converter="{cnv:FullPathToShortNameConverter}"
                                     Mode="OneWay" />
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
                <Setter Property="Text"
                        TargetName="txtToolTip">
                    <Setter.Value>
                        <MultiBinding StringFormat="{}[{1}] &#x0a; was renamed to &#x0a; {0} ">
                            <Binding Path="ServerPath"
                                     Mode="OneWay" />
                            <Binding Path="SourceServerItem"
                                     Mode="OneWay" />
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
            <DataTrigger Binding="{Binding State,Converter={cnv:EnumFlagConverter FlagValue='Locked'}, ConverterParameter={x:Type viewModels:SourceControlState}}"
                         Value="True">
                <Setter Property="Source"
                        TargetName="srcCtrlStatusIndicator"
                        Value="{StaticResource ImageSourceCheckedOutBySomeoneElse9x9}" />
                <Setter Property="Opacity"
                        TargetName="txt"
                        Value="0.5" />
                <Setter Property="Text"
                        TargetName="txtToolTip">
                    <Setter.Value>
                        <MultiBinding StringFormat="{} File is readonly and uneditable. &#x0a; Reason: it has been checked out by: [{1}] &#x0a; On machine {0}&#x0a; Change Type is {2}">
                            <Binding Path="PendingSetName"
                                     Mode="OneWay" />
                            <Binding Path="PendingSetOwner"
                                     Mode="OneWay" />
                            <Binding Path="State"
                                     Mode="OneWay" />
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>

在 XAML 中,您可以通过触发器和绑定找到不同的数据表示方法.

In XAML you can find different approaches of data presentation by triggers and bindings.

上面的代码有结果.它通过不同的图标、UI 更改和带有信息的工具提示来表示所有状态.

There is result of the code above. It represents all states by different icons, UI changes and tooltips with information.

这篇关于C# TFS API:显示带有文件夹和文件的项目结构,包括它们的 ChangeType(签出、删除、重命名),就像在 Visual Studio 中一样的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

更多推荐

[db:关键词]

本文发布于:2023-04-30 18:02:34,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1398102.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:就像   文件夹   重命名   结构   文件

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!