ListBox ItemTemplate - 显示用户在运行时选择的属性

时间:2015-08-27 12:39:55

标签: c# wpf xaml listbox

我的ListBoxList DataModel绑定。

DataModel.cs

public class DataModel
{
    public string Name { get; set; }
    public string Desc { get; set; }
    public string Code { get; set; }
}

ListBox应显示两个属性,因此我已将ItemTemplate定义如下

<ListBox.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding Name}"></TextBlock>
            <TextBlock Text=" - "></TextBlock>
            <TextBlock Text="{Binding Code}"></TextBlock>
        </StackPanel>
    </DataTemplate>
</ListBox.ItemTemplate>

我的要求是用户可以选择在运行时在ListBox中显示哪两个属性。我不知道如何实现这一目标。我已经创建了一个示例解决方案来解释我的问题。

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="5">
            <Label Content="Property One"></Label>
            <ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
            <Label Content="Property Two"></Label>
            <ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
            <Button Content="Go" Click="ButtonBase_OnClick" Margin="3"></Button>
        </StackPanel>
        <ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}"></TextBlock>
                        <TextBlock Text=" - "></TextBlock>
                        <TextBlock Text="{Binding Code}"></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            DataContext = new ViewModel();

        }

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {

        }
    }

    public class ViewModel
    {
        public List<String> DataModelProperties { get; set; }

        public List<DataModel> DataModelList { get; set; }

        public ViewModel()
        {
            DataModelList = new List<DataModel>();

            DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1" });
            DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2" });
            DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3" });

            DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
        }
    }

    public class DataModel
    {
        public string Name { get; set; }
        public string Desc { get; set; }
        public string Code { get; set; }
    }
}

我尝试过的事情

<TextBlock Text="{Binding ElementName=ComboBox1, Path=SelectedItem}"></TextBlock>

这只显示所选的属性名称。

3 个答案:

答案 0 :(得分:0)

简而言之,我所做的就是将2个公共属性绑定到并将属性更改为字段。我还制作了自定义属性,以控制用户可以选择的字段。并创建了一个DisplayOptions类来存储选择并将其分布在DataModel实例中。 考虑到它是一个PoC,解决方案有点混乱,但我相信这可以做到:

XAML:

  <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="5">
            <Label Content="Property One"></Label>
            <ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
            <Label Content="Property Two"></Label>
            <ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"></ComboBox>
            <Button Content="Go" Click="ButtonBase_OnClick" Margin="3"></Button>
        </StackPanel>
        <ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding DisplayProperty1}"></TextBlock>
                        <TextBlock Text=" - "></TextBlock>
                        <TextBlock Text="{Binding DisplayProperty2}"></TextBlock>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>

CS:

 public partial class MainWindow : Window
    {
        public ViewModel model = new ViewModel();
        public MainWindow()
        {
            InitializeComponent();
            DataContext = model;
        }

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            //This part has to be on the View side, but this is PoC
            model.Opts.Prop1 = typeof(DataModel).GetFields()
                .Where(a => a.Name == ComboBox1.SelectedItem.ToString()).FirstOrDefault();
            model.Opts.Prop2 = typeof(DataModel).GetFields()
                .Where(a => a.Name == ComboBox2.SelectedItem.ToString()).FirstOrDefault();
            DataContext = null;
            DataContext = model;
        }
    }

    public class ViewModel
    {
        public DisplayOptions Opts = new DisplayOptions();

        public List<String> DataModelProperties { get; set; }

        public List<DataModel> DataModelList { get; set; }

        public ViewModel()
        {
            var properties = typeof(DataModel).GetFields()
              .Where(a => a.CustomAttributes.Any(b => b.AttributeType == typeof(SwitchableAttr)));

            //Initialising options before creating DataModel instances
            DataModelProperties = properties.Select(s => s.Name).ToList();
            Opts.Prop1 = properties.ElementAt(0);
            Opts.Prop2 = properties.ElementAt(1);


            DataModelList = new List<DataModel>();
            DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1", options = Opts });
            DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2", options = Opts });
            DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3", options = Opts });
        }
    }

    public class DisplayOptions
    {
        public FieldInfo Prop1;
        public FieldInfo Prop2;
    }

    public class DataModel
    {
        public DisplayOptions options;
        [SwitchableAttr]
        public string Name;
        [SwitchableAttr]
        public string Desc;
        [SwitchableAttr]
        public string Code;

        public string DisplayProperty1 { get { return (string)options.Prop1.GetValue(this); } set { } }
        public string DisplayProperty2 { get { return (string)options.Prop2.GetValue(this); } set { } }
    }

    public class SwitchableAttr : Attribute { }

答案 1 :(得分:0)

我建议你管理关于&#34;动态描述的所有内容&#34;显示在ViewModel的ListBox中。

首先,您的视图模型和模型应该实现INotifyPropertyChanged。在我的示例中,我创建了一个实现它的简单基类。我的基类名为NotifyPropertyChangedImpl

此外,我在视图模型中添加了两个属性:用于选择属性的组合框被绑定到这两个属性。

public class ViewModel : NotifyPropertyChangedImpl
{
    private string property1;
    private string property2;

    public List<String> DataModelProperties { get; set; }

    public List<DataModel> DataModelList { get; set; }

    public string Property1
    {
        get
        {
            return property1;
        }
        set
        {
            if (property1 != value)
            {
                property1 = value;
                SetDynamicDescriptions();
            }
        }
    }

    public string Property2
    {
        get
        {
            return property2;
        }
        set
        {
            if (property2 != value)
            {
                property2 = value;
                SetDynamicDescriptions();
            }
        }
    }

    public ViewModel()
    {
        DataModelList = new List<DataModel>();

        DataModelList.Add(new DataModel() { Name = "Name1", Code = "Code1", Desc = "Desc1" });
        DataModelList.Add(new DataModel() { Name = "Name2", Code = "Code2", Desc = "Desc2" });
        DataModelList.Add(new DataModel() { Name = "Name3", Code = "Code3", Desc = "Desc3" });

        DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
    }

    private void SetDynamicDescriptions()
    {
        PropertyInfo propertyInfo1;
        PropertyInfo propertyInfo2;

        Type type = typeof(DataModel);

        if (!String.IsNullOrEmpty(property1) && !String.IsNullOrEmpty(property2))
        {
            propertyInfo1 = type.GetProperty(property1);
            propertyInfo2 = type.GetProperty(property2);

            foreach (DataModel dataModel in DataModelList)
            {
                dataModel.DynamicDescription = String.Format("{0} - {1}",
                    propertyInfo1.GetValue(dataModel, null), propertyInfo2.GetValue(dataModel, null));
            }
        }
    }
}

正如您所看到的,方法SetDynamicDescriptions每次Property1或Property2更改时都会重建DynamicsDescription。

我还在模型类中添加了一个属性:

public class DataModel : NotifyPropertyChangedImpl
{
    private string dynamicDescription;

    public string Name { get; set; }
    public string Desc { get; set; }
    public string Code { get; set; }

    public string DynamicDescription
    {
        get
        {
            return dynamicDescription;
        }
        set
        {
            if (dynamicDescription != value)
            {
                dynamicDescription = value;
                OnPropertyChanged("DynamicDescription");
            }
        }
    }
}

所以最后你的XAML将是:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <StackPanel Orientation="Horizontal" Margin="5">
        <Label Content="Property One"></Label>
        <ComboBox Name="ComboBox1" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"
                    SelectedItem="{Binding Property1}"></ComboBox>
        <Label Content="Property Two"></Label>
        <ComboBox Name="ComboBox2" Margin="3" Width="150" ItemsSource="{Binding DataModelProperties}"
                    SelectedItem="{Binding Property2}"></ComboBox>

    </StackPanel>
    <ListBox Grid.Row="1" ItemsSource="{Binding DataModelList}" Margin="5">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding DynamicDescription}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

我希望这可以帮到你。

答案 2 :(得分:0)

这不是完整的MVVM 所以这是代码

public partial class MainWindow : Window
{
    private ViewModel vm;
    public MainWindow()
    {
        InitializeComponent();

        vm = new ViewModel();
        this.DataContext = vm;

    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        if (!string.IsNullOrEmpty(ComboBox1.Text) && !string.IsNullOrEmpty(ComboBox2.Text))
        {
            vm.AddDataToModel(ComboBox1.Text, ComboBox2.Text);
        }
    }
}

public class ViewModel
{
    public List<String> DataModelProperties { get; set; }


    private ObservableCollection<DataModel> _DataModelList;


    public ViewModel()
    {
        _DataModelList = new ObservableCollection<DataModel>();

        DataModelProperties = typeof(DataModel).GetProperties().Select(s => s.Name).ToList();
    }
    public void AddDataToModel(string cmbx1Val,string cmbx2Val)
    {
        _DataModelList.Add(new DataModel() { Name = cmbx1Val, Code = cmbx2Val, Desc = "Desc1" });
    }
    public ObservableCollection<DataModel> DataModelList
    {

        get
        {
            return _DataModelList;
        }
        set
        {
            _DataModelList = value;

        }

    }



}