【问题标题】:ListBox ItemTemplate - Display the property that the user selects at run timeListBox ItemTemplate - 显示用户在运行时选择的属性
【发布时间】:2015-08-27 12:39:55
【问题描述】:

我有一个ListBox,它绑定到DataModel 的List。

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>

这只是显示选定的属性名称。

【问题讨论】:

    标签: c# wpf xaml listbox


    【解决方案1】:

    我建议您管理 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】:

      简而言之,我所做的是创建了 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 { }
      

      【讨论】:

        【解决方案3】:

        这不是完整的 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;
        
                }
        
            }
        
        
        
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多