【问题标题】:Binding a ListBox specific item background to a specific value将 ListBox 特定项背景绑定到特定值
【发布时间】:2010-10-12 08:38:37
【问题描述】:

我是 WPF 的新手,仍在使用绑定等,所以这可能是一个基本问题。

考虑以下类:

class DataHolder
{
    public List<int> Data;

    // Points to one of the items in Data. Thus can be between 0 and Data.Count
    public int Pointer;
}

我已将 ListBox 的 DataContext 设置为上述类的一个实例,它的 ItemSource 是该实例的“数据”。

现在我想在 ListBox 中将包含 Data[Pointer] 的列表项的颜色标记为灰色。

我应该使用什么?我试过用 DataTriggers 来做,但我不确定如何使用它们来比较两个不同的值。在这一点上,我宁愿不使用 IValueConverter,除非不可能这样做。

编辑:随意将公共数据转换为上述类中的属性

谢谢!

【问题讨论】:

    标签: c# wpf data-binding


    【解决方案1】:

    我之前通过创建我称之为 DataViewModel 的方式完成了类似的操作。这基本上是集合中每个项目的 ViewModel。我用你的例子快速尝试了一下,它奏效了。我将在下面粘贴我所有的代码,如果您愿意,应该可以将其拉入并执行它。

    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"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
    
    <Window.DataContext>
        <local:MainWindow_ViewModel/>
    </Window.DataContext>
    <StackPanel>
        <TextBox Text="{Binding Pointer, Mode=TwoWay}" Height="20" Width="100"/>
        <ListBox ItemsSource="{Binding MyDataHolder}" Height="20" Width="100">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Value}" Background="{Binding Color}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
    

    MainWindow_ViewModel.cs

    public class MainWindow_ViewModel
    {
        public MainWindow_ViewModel() 
        {
            data = new DataHolder();
    
            foreach (int value in data.Data)
            {
                myData.Add(new Data_DataViewModel() { Value = value });
            }
    
            this.Pointer = 4;
        }
    
        private DataHolder data;
        private List<Data_DataViewModel> myData = new List<Data_DataViewModel>();
    
        public List<Data_DataViewModel> MyDataHolder
        {
            get
            {
                return myData;
            }
        }
    
        public int Pointer
        {
            get { return this.data.Pointer; }
            set 
            {
                this.data.Pointer = value;
                foreach (Data_DataViewModel dvm in this.myData)
                {
                    dvm.UpdateColor(this.data.Pointer);
                }
            }
        }
    }
    

    DataHolder.cs

    public class DataHolder
    {
        public List<int> Data
        {
            get { return data; }   
        }
        private List<int> data = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8 };
    
        public int Pointer = 3;
    }
    

    Data_DataViewModel.cs

    public class Data_DataViewModel : INotifyPropertyChanged
    {
        public int Value
        {
            get { return val; }
            set { val = value; } 
        }
        private int val;
    
        public Brush Color
        {
            get 
            {
                if (this.Value == pointer)
                {
                    return Brushes.Gray;
                }
                else
                {
                    return Brushes.Pink;
                }
            }
        }
    
        private int pointer;
    
        public void UpdateColor(int pointerValue)
        {
            this.pointer = pointerValue;
            OnPropertyChanged("Color");
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    【讨论】:

    • 这是一个有趣的解决方案,谢谢。我用一些我不知道的复杂绑定语法来想象它。但是,从示例中不清楚,您是否使用“数据”中的数据初始化“myData”?如果是这样,您需要将相同的数据存储两次,这真是太可惜了。
    • 哦,好吧,我错过了初始初始化。现在有道理了:)谢谢。
    【解决方案2】:

    Listbox 还有一个SelectedIndex 可以绑定的属性。使索引成为 ViewModel 上的属性(提示:数据属性,还要确保该属性触发 notify change event)。然后编辑列表框项的Selected State

    IMO 比其他提案容易得多,并且适当地使用模板和绑定更多

    【讨论】:

    • 我认为您误解了这个问题。也许OP可以纠正我。如果将 SelectedIndex 绑定到某个属性,则会选择该特定 ListItem。 OP 从不提及选择项目,只是根据 DataHolder 中的索引/指针更改背景
    • 没错。因此,由于列表是绑定的,并且他想在索引处为项目着色,然后更改选定状态以使项目变为灰色,从而实现了他的目标。
    • 您假设DataHolder.Pointer 包含当前选定值的索引。实际上并没有具体说明。
    • 显然我很困惑。我选择了“现在我想在 ListBox 中将包含 Data[Pointer] 的列表项的颜色标记为灰色。”就是这个意思。
    • 感谢 Ragepotato 的回答,但正如 SKG/Robert 所说,指针变量可能与所选值不同。
    【解决方案3】:

    有趣的问题。我的解决方案使用 ItemTemplateSelector。在其 SelectTemplate 覆盖中,您可以访问 ListItem 所在的面板 (VirtualizingStackPanel)。这里的技巧是,当每个 ListItem 添加到 ListBox 时,就会调用这个覆盖,我们可以使用 ChildCount 来确定它在 ListBox 中的索引。这有助于我们比较和选择正确的模板。

    Main.xaml

    <Window
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApplication1" mc:Ignorable="d" x:Class="WpfApplication1.MainWindow"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>       
            <DataTemplate
                x:Key="DataTemplateWithHighlight">
                <StackPanel>
                    <TextBlock HorizontalAlignment="Stretch"
                        Text="{Binding Mode=OneWay}"
                        Background="Gray" />
                </StackPanel>
            </DataTemplate>
            <DataTemplate
                x:Key="DataTemplateWithoutHighlight">
                <StackPanel>
                    <TextBlock
                        HorizontalAlignment="Stretch"
                        Text="{Binding Mode=OneWay}"
                        Background="White" />
                </StackPanel>
            </DataTemplate>
            <local:ListBoxItemTemplateSelector
                x:Key="listBoxItemTemplateSelector"
                HighlightedItemTemplate="{StaticResource DataTemplateWithHighlight}"
                NonHighlightedItemTemplate="{StaticResource DataTemplateWithoutHighlight}" />
    
        </Window.Resources>
        <StackPanel Orientation="Vertical" d:LayoutOverrides="Height" HorizontalAlignment="Center" VerticalAlignment="Center">
            <ListBox
                x:Name="listBoxWithMeaninglessNumbers"
                Height="100"
                Width="100"
                ItemsSource="{Binding Data}"
                ItemTemplateSelector="{DynamicResource listBoxItemTemplateSelector}">          
            </ListBox>
        </StackPanel>
    </Window>
    

    数据项类

     public  class DataItems
        {
          public List<int> Data { get; set;}
          public int HighlightedIndex { get; set; }
        }
    

    一些初始数据可以帮助您前进

    public static class DataStub
    {
            public static DataItems TestDataItems
            {
                get
                {
                    var dataItems = new DataItems();
                    dataItems.Data = new List<int>(){1,5,9,6,8};
                    dataItems.HighlightedIndex = 2;
    
                    return dataItems;
                }
            }
    }
    

    MainWindow.Xaml.cs

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            listBoxWithMeaninglessNumbers.DataContext = DataStub.TestDataItems;
        }
    }
    

    模板选择器类:

     public class ListBoxItemTemplateSelector:DataTemplateSelector
        {
           public DataTemplate NonHighlightedItemTemplate { get; set; }
           public DataTemplate HighlightedItemTemplate { get; set; }
    
    
    
             public override DataTemplate SelectTemplate(object item, DependencyObject container)
           {
                 var listBoxItem = ((FrameworkElement) container).TemplatedParent as ListBoxItem;
                 var panel = VisualTreeHelper.GetParent(listBoxItem) as Panel;
                 var highlightedIndex = (panel.DataContext as DataItems).HighlightedIndex;
                 var currentChildIndex = panel.Children.Count-1;
    
                 return (highlightedIndex == currentChildIndex) ? HighlightedItemTemplate : NonHighlightedItemTemplate;
                }
        }
    }
    

    希望这会有所帮助。

    【讨论】:

    • 有趣!从你的例子中学到了很多。不确定我是按照您描述的方式还是其他评论,但谢谢!
    • 但是,您的示例的一个问题是您无法在运行时更改 currentChildIndex(无需删除并重新添加所有项目)。关于如何克服它的任何想法?我意识到这不是原始问题的先决条件,但现在我很好奇。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-12
    • 1970-01-01
    • 2013-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多