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

本文关键字:运行时 选择 属性 用户 ItemTemplate 显示 ListBox | 更新日期: 2023-09-27 18:10:01

我有一个与DataModelList绑定的ListBox

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>

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

我建议你管理所有的东西关于"动态描述"显示在你的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个公共属性来绑定,并将属性更改为字段。此外,我还制作了自定义属性,以控制用户能够选择哪些字段。并创建了一个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 { }

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

}