Search code examples
wpfattached-properties

WPF AttachedProperty


I have created a simple test project which creates a ListBox populated with Checkboxes. I have some codebehind that whenever the checkboxes are checked the TextBox above the ListBox displays the text of all the selected items. Everything works but I am now trying to create an Attached Property (again in the code behind) that allows a prefix to be included. Eventually I am looking to convert this to MVVM standards but because I am relatively new to WPF and very new to MVVM I like to work from a working sample project and gradually convert each part to an MVVM project as I find this helps me understand better what is going on. My code is below:

xaml:

<Window x:Class="CheckBoxList.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:CheckBoxList"
    mc:Ignorable="d"
    Title="MainWindow" Height="400" Width="400">
<Window.Resources>
    <Style x:Key="CheckBoxListStyle" TargetType="{x:Type ListBox}">
        <Setter Property="SelectionMode" Value="Multiple"></Setter>
        <Setter Property="ItemContainerStyle">
            <Setter.Value>
                <Style TargetType="{x:Type ListBoxItem}" >
                    <Setter Property="Margin" Value="2" />
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListBoxItem}">
                                <CheckBox
                                    IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
                                    Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
                                    <ContentPresenter></ContentPresenter>
                                </CheckBox>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid Margin="8">
    <Grid.RowDefinitions>
        <RowDefinition Height="30"/>
        <RowDefinition/>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="30"/>
    </Grid.RowDefinitions>
    <TextBox Grid.Row="0" x:Name="ListBoxTextBox"/>
    <ListBox Grid.Row="1"  Style="{StaticResource CheckBoxListStyle}" Name="lstProducts" local:MainWindow.PrefixMark=">"
         DisplayMemberPath="ModelName">
    </ListBox>
    <Button Grid.Row="2" Margin="0,5,0,0" Click="cmdGetSelectedItems">Get Selected Items</Button>
    <CheckBox Grid.Row="3" Content="Test" Margin="5"/>
</Grid>

xaml.cs

    public static string GetPrefixMark(DependencyObject obj)
        {
            string t = (string)obj.GetValue(PrefixMarkProperty);
            return (string)obj.GetValue(PrefixMarkProperty);
        }

        public static void SetPrefixMark(DependencyObject obj, string value)
        {
            obj.SetValue(PrefixMarkProperty, value);
            string t = (string)obj.GetValue(PrefixMarkProperty);
        }

        // Using a DependencyProperty as the backing store for PrefixMark.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PrefixMarkProperty =
            DependencyProperty.RegisterAttached("SetPrefixMark", typeof(string), typeof(MainWindow), new PropertyMetadata("('"));

private void CheckBox_Checked(object sender, RoutedEventArgs e)
        {
            if (sender is FrameworkElement element)
            {
                AddText(element);
            }
        }
private void AddText(FrameworkElement e)
        {
            StringBuilder sB = new StringBuilder("");
            foreach (Product product in lstProducts.SelectedItems)
            {
                sB.Append(product.ModelName).Append(ValueSeparator);
            }
            if (sB.Length > 0)
            {
                sB.Remove(sB.Length - (ValueSeparator).Length, (ValueSeparator).Length);
            }
            if (sB.Length > 0)
            {   
                ListBoxTextBox.Text = GetPrefixMark(e) + sB.ToString();
            }
            else
            {
                ListBoxTextBox.Text = "";
            }
        }

When I run the programme although the PrefixMark is set on Load when ever I check a checkbox it reverts back to the default. I can only presume this is being created twice but for the life of me I cannot figure out why and how to resolve the issue?


Solution

  • You should pass the actual name of the dependency property to the Register method to begin with:

    DependencyProperty.RegisterAttached("PrefixMark", ...
    

    The SetPrefixMark is no longer being fired on startup

    It's not supposed to. If you want to set the Text property of the TextBox at startup, you should either bind it to a source property or set it programmatically:

    public MainWindow()
    {
        InitializeComponent();
        ListBoxTextBox.Text = GetPrefixMark(this);
        ...
    }