Search code examples
wpfdependency-propertiesproperty-binding

In WPF how do I properly bind the style of a button within an ItemsControl template and use a converter?


In WPF, I am trying to have the button created as part of the ItemTemplate in a ItemsControl select the style using a property within the class. I thought I had it coded correctly, but when I run this, it creates the button "Test", but it never even runs the converter for the Style.

By the way, yes, I'm obviously aware that I haven't properly implemented the IValueConverter yet, but when I set breakpoints, it never even enters the converter.

Also, how do I properly keep the style of the button updated when the value of the property CurrentItemProperty changes?

MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
        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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <local:ButtonStyleConverter x:Key="ButtonStyleConverter"/>
    </Window.Resources>



    <Grid>
        <ItemsControl x:Name="ButtonList">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="OptionButton">
                    <Button Content="{Binding DisplayName}" Style="{Binding CurrentItem, Converter={StaticResource ButtonStyleConverter}}"></Button>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

    </Grid>




</Window>

MainWindow.xaml.cs

namespace WpfApp1
{


    public partial class MainWindow : Window
    {

        DependencyProperty CurrentItemProperty = DependencyProperty.Register("CurrentItemProperty", typeof(string), typeof(MainWindow));
        public string CurrentItem
        {
            get
            {
                return (string)GetValue(CurrentItemProperty);
            }
            set
            {
                SetValue(CurrentItemProperty, value);
            }
        }



        public MainWindow()
        {
            InitializeComponent();

            ButtonList.ItemsSource = new OptionButton[]
            {
                new OptionButton() { DisplayName = "Test"}
            };
            CurrentItem = "Test";
        }



        public class OptionButton
        {
            public string DisplayName { get; set; }
        }





    }

    public class ButtonStyleConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

}

Solution

  • when I set breakpoints, it never even enters the converter.

    Because there's nothing to convert.

    If you would run your code in the debugger and look at the debug output, you'd find that you're getting a binding error, where the binding engine fails to find the property CurrentItem on the source object. That's because the source object is a OptionButton instance, which of course does not have a CurrentItem property.

    You have a number of options available to you:

    • Control the style some other way. There's not enough context in your question to know why you're trying to use a converter to return a style, but it's unusual enough an approach that I'll suggest this should be your first thing to try. There's probably a better way to control the styling of the button than using a converter this way.
    • Use a property that's on the OptionButton type. In your example, the CurrentItem and OptionButton.DisplayName have the same value, so taking the example literally you could just bind to the DisplayName property instead. Alternatively, maybe you could implement a property in OptionButton that delegates to the parent-level value you care about.
    • Set the binding source to the window, so that the CurrentItem property is visible. There are (at least) two variations on this theme: give the window a name attribute, and refer to it by name in the binding, or just use {RelativeSource AncestorType=Window} markup for the source of the binding.

    The bottom line is that the binding doesn't work as expected because the current data context for the binding (which defines the default source for the binding) doesn't have the property you're binding to.