开发者

Binding a ListBox specific item background to a specific value

I'm rather a newbie with WPF and still playing with bindings and such, so this probably a basic question.

Consider the following class:

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;
}

I've set a ListBox's DataContext to be an instance of the class above and its ItemSource is the 'Data' of the instance.

Now I'd like to mark in the ListBox the color of the list item that holds Data[Pointer] to be gray color.

What should I use? I've tried doing it with DataTriggers but I am not sure开发者_运维技巧 how to compare two different values using them. I'd rather not use IValueConverter at this point, unless it is impossible to do otherwise.

EDIT: Feel free to convert the public data to properties in the mentioned class

Thanks!


I've done things like this before by creating what I call a DataViewModel. This is basically a ViewModel for each item in the collection. I tried it quick with your example and it worked. I'll paste all my code below, should be able to pull it in and execute it if you wish.

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));
        }
    }
}


Listbox also has a SelectedIndex property that you can bind to. Make the index a property on your ViewModel (Hint: the data property, also make sure the property fires a notify change event). Then edit the Selected State of the listbox item.

IMO far easier than the other proposals and uses the templating and binding more appropriately.


Interesting question. My solution uses ItemTemplateSelector. In its SelectTemplate override, you can access the Panel (VirtualizingStackPanel) under which the ListItems are housed. The trick here is that as each ListItem is added to the ListBox, this override is called and we can use the ChildCount to determinate its index in the ListBox. This helps us in comparison and selecting the right template.

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>

DataItems Class

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

Some initial data to get you going

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;
    }
}

TemplateSelector Class:

 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;
            }
    }
}

Hope this helps.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜