0

我有从服务中收集到的有关 TFS 构建放入 ViewModels 的信息。以下是模型:

public class Collection : ViewModel
{
    private string _name = string.Empty;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged(() => Name);
        }
    }

    private ObservableCollection<Project> _projects = new ObservableCollection<TFSProject>();

    public ObservableCollection<Project> Projects
    {
        get { return _projects; }
        set
        {
            _projects = value;
            OnPropertyChanged(() => Projects);
        }
    }

}

public class Project : ViewModel
{
    private string _name = string.Empty;

    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged(() => Name);
        }
    }

    private ObservableCollection<string> _buildDefinitions = new ObservableCollection<string>();

    public ObservableCollection<string> BuildDefinitions
    {
        get { return _buildDefinitions; }
        set
        {
            _buildDefinitions = value;
            OnPropertyChanged(() => BuildDefinitions);
        }
    }
}

我将组合框的 itemssource 绑定到ObservableCollection<Collection>. 问题是集合、项目和构建定义名称存储在一个类中,该类将它们定义为单独的字符串属性:

public class BuildMonitor : INotifyPropertyChanged
{
    [Description("In TFS, each team project exists within a TFS Collection.  This is the name of the collection applicable to the build that this monitor is for. Default='Vision2010'")]
    public string Collection
    {
        get { return collection_; }
        set
        {
            collection_ = value;
            OnPropertyChanged(() => Collection);
        }
    }
    private string collection_ = "Vision2010";

    [Description("BuildDefintions reside within a TeamProject. This is the name of the TeamProject where the build definition this monitor is for resides. Default='Double-Take2010'")]
    public string TeamProject { get { return teamProject_; } set { teamProject_ = value; OnPropertyChanged(() => TeamProject); } }
    private string teamProject_ = "Double-Take2010";

    [Description("Builds are defined in TFS as the execution of a particular BuildDefinition.  This is the name of the build defintion (thus; the build) this monitor is for.")]
    public string BuildDefinition { get { return buildDefinition_; } set { buildDefinition_ = value; OnPropertyChanged(() => BuildDefinition); } }
    private string buildDefinition_;

    [Description("Used only if this monitor should watch for builds specified by a particular user.  Enter the domain name of the user, or leave blank to monitor builds by any user.")]
    public string RequestedByFilter { get { return requestedByFilter_; } set { requestedByFilter_ = value; OnPropertyChanged(() => RequestedByFilter); } }
    private string requestedByFilter_;

    [Description("The command to execute when the build monitor is triggered.")]
    public string Command { get { return command_; } set { command_ = value; OnPropertyChanged(() => Command); } }
    private string command_;

    [Description("The arguments to pass to the command.  Arguments will resolve known build monitor macros.")]
    public string Arguments { get { return arguments_; } set { arguments_ = value; OnPropertyChanged(() => Arguments); } }
    private string arguments_;

    [Description("If TRUE, the monitor will fire only once, at which point it will be marked as 'invalid' and never fire again.")]
    public bool RunOnce { get { return runOnce_; } set { runOnce_ = value; OnPropertyChanged(() => RunOnce); } }
    private bool runOnce_ = false;

    [Description("The maximum age (in hours) a build can be (since finished), for the monitor to consider it for processing. Default='0'")]
    public int MaxAgeInHours { get { return maxAgeInHours_; } set { maxAgeInHours_ = value; OnPropertyChanged(() => MaxAgeInHours); } }
    private int maxAgeInHours_ = 0;

    [Description("Which status trigger the monitor should 'fire' on.  When the build status matches this trigger, the monitor command will be executed. Default='Succeeded'")]
    public BuildStatus EventTrigger { get { return eventTrigger_; } set { eventTrigger_ = value; OnPropertyChanged(() => EventTrigger); } }
    private BuildStatus eventTrigger_ = BuildStatus.Succeeded;

    [Browsable(false), Description("Used internally to reliably compare two BuildMonitors against each other.")]
    public Guid ID { get { return id_; } set { id_ = value; } }
    private Guid id_ = Guid.NewGuid();

    [Browsable(false), Description("Used internally to determine if the monitor is still valid/should be processed.")]
    public bool IsEnabled { get { return isEnabled_; } set { isEnabled_ = value; } }
    private bool isEnabled_ = true;

    [Browsable(false), XmlIgnore, Description("Used internally to track when the monitor is 'busy' (currently running the 'Command' selected.")]
    public int CurrentProcessID { get { return currentProcessID_; } set { currentProcessID_ = value; } }
    private int currentProcessID_ = 0;

    [Browsable(false), XmlIgnore, Description("Used internally to track the build that the monitor is currently processing.")]
    private string currentBuildUri_;
    public string CurrentBuildUri { get { return currentBuildUri_; } set { currentBuildUri_ = value; } }

    [field: NonSerialized, Browsable(false)]
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
    {
        MemberExpression memberExpression = (MemberExpression)propertyExpression.Body;
        string propertyName = memberExpression.Member.Name;

        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

在我的 xaml 中,我试图通过将集合组合框的 itemssource 设置为相对源绑定(即ObservableCollection<Collection>. 我得到了列表中的项目,但由于 itemssource 是 a List<BuildMonitors>,我似乎无法让所选项目将所选项目的 name 属性映射到数据项的实际绑定(BuildMonitor 实例中的字符串 Collection )。

<tk:DataGrid ItemsSource="{Binding Monitors}"
                AutoGenerateColumns="False">
<tk:DataGrid.Columns>       
    <tk:DataGridTemplateColumn Header="Collection">
        <tk:DataGridTemplateColumn.CellEditingTemplate>
            <DataTemplate>
                <ComboBox x:Name="Collection"
                                ItemsSource="{Binding Path=AllCollections, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type apollo:BuildMonitorNew}}}"
                                DisplayMemberPath="Name"
                                SelectedItem="{Binding .}"
                                SelectedValuePath="Collection"
                                SelectedValue="{Binding Name}"/>
            </DataTemplate>
        </tk:DataGridTemplateColumn.CellEditingTemplate>
        <tk:DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Collection,Mode=TwoWay}"/>
            </DataTemplate>
        </tk:DataGridTemplateColumn.CellTemplate>
    </tk:DataGridTemplateColumn>
    <tk:DataGridTemplateColumn Header="Project">
        <tk:DataGridTemplateColumn.CellEditingTemplate>
            <HierarchicalDataTemplate ItemsSource="{Binding ElementName=Collection,Path=SelectedItem.Projects}">
                <ComboBox x:Name="Projects"
                                ItemsSource="{Binding}"
                                DisplayMemberPath="Name"/>
            </HierarchicalDataTemplate>
        </tk:DataGridTemplateColumn.CellEditingTemplate>
    </tk:DataGridTemplateColumn>
    <tk:DataGridTextColumn Binding="{Binding Command}"
                                    Header="Command"/>
    <tk:DataGridTextColumn Binding="{Binding Arguments}"
                                    Header="Arguments"
                                    Width="*"/>
</tk:DataGrid.Columns>

我的第一个想法是,虽然我的视图模型可能更好地表示数据(分层),但要选择的数据结构与实际存储的数据的结构差异太大。

我很想在这里犯错,并找到一种时髦的方法来将实际选择的数据(集合、项目,然后是 BuildDefinition)转换为存储的数据的路径(BuildMonitor)。

有任何想法吗?

4

1 回答 1

0

我发现多值转换器允许我将层次结构转换为组合框项目源的部分,它们代表一个列表。

转换器:

public class BuildMonitorItemSource : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values.Count() == 2)
        {
            string collectionToGet = parameter.ToString();

            ObservableCollection<TFSCollection> allcollections = values[1] as ObservableCollection<TFSCollection>;

            BuildLauncher.BuildMonitor currentBM = (values[0] as BuildLauncher.BuildMonitor);

            if (collectionToGet.Equals("Projects"))
            {
                return allcollections.FirstOrDefault(x => x.Name.Equals(currentBM.Collection, StringComparison.OrdinalIgnoreCase)).Projects;

            }
            else if (collectionToGet.Equals("BuildDefinitions"))
            {
                TFSCollection currentCollection = allcollections.FirstOrDefault(x => x.Name.Equals(currentBM.Collection));

                TFSProject currentProject = currentCollection.Projects.FirstOrDefault(x => x.Name.Equals(currentBM.TeamProject));

                return currentProject.BuildDefinitions;
            }
        }
        return values;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
             throw new NotImplementedException();
    }
}

这是修改后的xml:

<tk:DataGrid ItemsSource="{Binding Monitors}"
                                 AutoGenerateColumns="False"
                                 IsEnabled="{Binding RelativeSource={RelativeSource AncestorType=apollo:BuildMonitorNew}, Path=TFSAuthenticated}"
                                 Name="MontiorsGrid">
                    <tk:DataGrid.Columns>
                        <tk:DataGridCheckBoxColumn Binding="{Binding IsEnabled}">
                            <tk:DataGridCheckBoxColumn.Header>
                                <Ellipse Grid.Column="0" 
                                                            HorizontalAlignment="Left" 
                                                            Height="10" Width="10"                                                              
                                                            Stroke="Black"
                                                            StrokeThickness="1"
                                                            Fill="Green"/>
                            </tk:DataGridCheckBoxColumn.Header>
                        </tk:DataGridCheckBoxColumn>
                        <tk:DataGridTemplateColumn Header="Collection">
                            <tk:DataGridTemplateColumn.CellEditingTemplate>
                                <DataTemplate>
                                    <ComboBox x:Name="CollectionCombo"
                                                 ItemsSource="{Binding Path=AllCollections, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type apollo:BuildMonitorNew}}}"
                                                 DisplayMemberPath="Name"   
                                                 SelectedValue="{Binding Collection}"
                                                 SelectedValuePath="Name">
                                    </ComboBox>
                                </DataTemplate>
                            </tk:DataGridTemplateColumn.CellEditingTemplate>
                            <tk:DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Collection,Mode=TwoWay}"/>
                                </DataTemplate>
                            </tk:DataGridTemplateColumn.CellTemplate>
                        </tk:DataGridTemplateColumn>
                        <tk:DataGridTemplateColumn Header="Project">
                            <tk:DataGridTemplateColumn.CellEditingTemplate>
                                <DataTemplate>
                                    <ComboBox x:Name="Projects"
                                                 DisplayMemberPath="Name"
                                                 SelectedValue="{Binding TeamProject}"
                                                 SelectedValuePath="Name">
                                        <ComboBox.ItemsSource>
                                            <MultiBinding Converter="{StaticResource GetItemSource}" ConverterParameter="Projects" >
                                                <Binding Path="." />
                                                <Binding Path="AllCollections" RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType={x:Type apollo:BuildMonitorNew}}"/>
                                            </MultiBinding>
                                        </ComboBox.ItemsSource>
                                    </ComboBox>
                                </DataTemplate>
                            </tk:DataGridTemplateColumn.CellEditingTemplate>
                            <tk:DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding TeamProject,Mode=TwoWay}"/>
                                </DataTemplate>
                            </tk:DataGridTemplateColumn.CellTemplate>
                        </tk:DataGridTemplateColumn>
                        <tk:DataGridTemplateColumn Header="Build Definition">
                            <tk:DataGridTemplateColumn.CellEditingTemplate>
                                <DataTemplate>
                                    <ComboBox SelectedItem="{Binding BuildDefinition}">
                                        <ComboBox.ItemsSource>
                                            <MultiBinding Converter="{StaticResource GetItemSource}" ConverterParameter="BuildDefinitions">
                                                <Binding Path="." />
                                                <Binding Path="AllCollections" RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType={x:Type apollo:BuildMonitorNew}}"/>
                                            </MultiBinding>
                                        </ComboBox.ItemsSource>
                                    </ComboBox>
                                </DataTemplate>
                            </tk:DataGridTemplateColumn.CellEditingTemplate>
                            <tk:DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding BuildDefinition,Mode=TwoWay}"/>
                                </DataTemplate>
                            </tk:DataGridTemplateColumn.CellTemplate>
                        </tk:DataGridTemplateColumn>
                        <tk:DataGridTextColumn Binding="{Binding Command}"
                                                      Header="Command"/>
                        <tk:DataGridTextColumn Binding="{Binding Arguments}"
                                                      Header="Arguments"
                                                      Width="*"/>
                    </tk:DataGrid.Columns>
                </tk:DataGrid>
于 2012-05-17T19:41:53.183 回答