Search code examples
wpfxamldata-bindingwpf-controlswpfdatagrid

In WPF I can't get the two way binding to work on DataGrid for User Control to Observable collection


I have a custom UserControl that is modified to have 3 state. It is tied to a binding with my own dependency property of the user control and Tag property with ObservableCollection data to a DataGrid. I don't know why my data is not updating when changed. I set it to two way bind. I am using ItemCollectionViewSource for data binding. Here is my code.

My DataGrid

<DataGrid x:Name="datagrdControlListGrid" AutoGenerateColumns="False" Margin="0" CanUserSortColumns="False"  AlternatingRowBackground="Gainsboro"  AlternationCount="2" DataContext="{StaticResource ItemCollectionViewSource}" ItemsSource="{Binding ''}" MinColumnWidth="50" GridLinesVisibility="None">
            <DataGrid.Columns>

                <DataGridTemplateColumn Header="Join"  Width="SizeToCells" IsReadOnly="False" CanUserSort="True" SortDirection="Ascending">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox Width="auto" Text="{Binding JoinNum, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Control Name" Width="SizeToCells" CanUserSort="True"  SortDirection="Descending" >
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox Width="auto" Text="{Binding ControlName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Type" Width="SizeToCells" CanUserSort="True" >
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox Width="auto" Text="{Binding ControlType, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Description" Width="SizeToCells" >
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox Width="auto" Text="{Binding ControlDesc, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Result" Width="SizeToCells" >
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <!--<CheckBox  IsThreeState="True" Width="20" Height="20" />-->
                            <Grid  Height="32"  Width="85">
                                <UserControls:FancyButton Width="82" Tag="{Binding Result , Mode=TwoWay}" 
                                                          ButtonState="{Binding Result, Mode=TwoWay }" Height="35" />
                            </Grid>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Comment" Width="SizeToCells" >
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBox Width="170" Text="{Binding Comment, Mode=TwoWay,  UpdateSourceTrigger=LostFocus}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>

            </DataGrid.Columns>
        </DataGrid>

Here is my usercontrol XAML

<UserControl x:Class="caM.UserControls.FancyButton"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:caM.UserControls"
         mc:Ignorable="d" Width="37" Height="31">
<UserControl.Resources>

</UserControl.Resources>
<Grid>
    <Button x:Name="HelloButton" Click="Button_Click"  Tag="{Binding Path= ButtonState , Mode=TwoWay ,UpdateSourceTrigger=PropertyChanged}" Margin="0" LostFocus="HelloButton_LostFocus">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Style.Triggers>
                    <Trigger Property="Tag" Value="None">
                        <Setter Property="Content" Value="Image1"/>
                        <Setter Property="Content">
                            <Setter.Value>
                                <Grid>
                                    <Rectangle Width="Auto" Height="Auto" Tag="None" RadiusY="18" RadiusX="18">
                                        <Rectangle.Fill>
                                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                <GradientStop Color="#FFBBBBBB" Offset="0.004"/>
                                                <GradientStop Color="#FFBBBBBB" Offset="0.992"/>
                                                <GradientStop Color="#FFEEEEEE" Offset="0.209"/>
                                                <GradientStop Color="#FFEAEAEA" Offset="0.771"/>
                                                <GradientStop Color="White" Offset="0.478"/>
                                            </LinearGradientBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                </Grid>

                            </Setter.Value>
                        </Setter>
                    </Trigger>
                    <Trigger Property="Tag" Value="On">
                        <Setter Property="Content" Value="Image2"/>
                        <Setter Property="Content">
                            <Setter.Value>
                                <Grid>
                                    <Rectangle Width="Auto" Height="Auto" Tag="None" RadiusY="18" RadiusX="18">
                                        <Rectangle.Fill>
                                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                <GradientStop Color="#FF2A9C25" Offset="0.004"/>
                                                <GradientStop Color="#FF2A9C25" Offset="0.992"/>
                                                <GradientStop Color="#FF8DE28D" Offset="0.209"/>
                                                <GradientStop Color="#FFA3E6A0" Offset="0.763"/>
                                                <GradientStop Color="#FFC9FFC6" Offset="0.482"/>
                                            </LinearGradientBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                </Grid>

                            </Setter.Value>
                        </Setter>
                    </Trigger>
                    <Trigger Property="Tag" Value="Off">
                        <Setter Property="Content" Value="Image3"/>
                        <Setter Property="Content">
                            <Setter.Value>
                                <Grid>
                                    <Rectangle Width="Auto" Height="Auto" Tag="None" RadiusY="18" RadiusX="18">
                                        <Rectangle.Fill>
                                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                <GradientStop Color="#FF9C2525" Offset="0.004"/>
                                                <GradientStop Color="#FF9C2525" Offset="0.992"/>
                                                <GradientStop Color="#FFE28D8D" Offset="0.209"/>
                                                <GradientStop Color="#FFE6A0A0" Offset="0.763"/>
                                                <GradientStop Color="#FFFFC6C6" Offset="0.482"/>
                                            </LinearGradientBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                </Grid>

                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
        <Button.Template>
            <ControlTemplate TargetType="{x:Type Button}">

                <Grid>
                    <Rectangle Width="Auto" Height="Auto" Tag="None" RadiusY="18" RadiusX="18">
                        <Rectangle.Fill>
                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="#FFBBBBBB" Offset="0.004"/>
                                <GradientStop Color="#FFBBBBBB" Offset="0.992"/>
                                <GradientStop Color="#FFEEEEEE" Offset="0.209"/>
                                <GradientStop Color="#FFEAEAEA" Offset="0.771"/>
                                <GradientStop Color="White" Offset="0.478"/>
                            </LinearGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <ContentPresenter Content="{TemplateBinding Content}"/>
                </Grid>
            </ControlTemplate>
        </Button.Template>
    </Button>

</Grid>

Here is my UserControl.xaml.cs

public partial class FancyButton : UserControl
    {
        public FancyButton()
        {
            InitializeComponent();
            this.DataContext = this;
            Tag = "None";
            ButtonState = "None";
        }



        public string ButtonState 
        {
            get { return (string)GetValue(ButtonStateProperty); }
            set { SetValue(ButtonStateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ButtonState.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ButtonStateProperty =
            DependencyProperty.Register("ButtonState", typeof(string), typeof(FancyButton), new PropertyMetadata(""));




        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var roaw = ((FrameworkElement)sender).DataContext as DataRowView;


            if (HelloButton.Tag.ToString() == "None")
            {
                HelloButton.Tag = "On";
                ButtonState = "On";
            }
            else if (HelloButton.Tag.ToString() == "On")
            {
                HelloButton.Tag = "Off";
                ButtonState = "Off";
            }
            else if (HelloButton.Tag.ToString() == "Off")
            {
                HelloButton.Tag = "None";
                ButtonState = "None";
            }
            else
            {
                HelloButton.Tag = "On";
                ButtonState = "On";
            }
        }

        private void HelloButton_LostFocus(object sender, RoutedEventArgs e)
        {

        }
    }

Solution

  • You don't need to use the Tag property when you have defined your own custom ButtonState property. Also, you are overriding the DataContext of the UserControl. Don't do this. This code should work better:

    public partial class FancyButton : UserControl
    {
        public FancyButton()
        {
            InitializeComponent();
        }
    
        public string ButtonState
        {
            get { return (string)GetValue(ButtonStateProperty); }
            set { SetValue(ButtonStateProperty, value); }
        }
    
        public static readonly DependencyProperty ButtonStateProperty =
            DependencyProperty.Register("ButtonState", typeof(string), typeof(FancyButton), new PropertyMetadata(""));
    
        private void Button_Click(object sender, RoutedEventArgs e)
        {
    
            if (ButtonState == "None")
            {
                ButtonState = "On";
            }
            else if (ButtonState == "On")
            {
                ButtonState = "Off";
            }
            else if (ButtonState == "Off")
            {
                ButtonState = "None";
            }
            else
            {
                ButtonState = "On";
            }
        }
    
        private void HelloButton_LostFocus(object sender, RoutedEventArgs e)
        {
    
        }
    }
    

    XAML:

    <Button x:Name="HelloButton" Click="Button_Click" Margin="0" LostFocus="HelloButton_LostFocus">
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ButtonState, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="None">
                        <Setter Property="Content" Value="Image1"/>
                        <Setter Property="Content">
                            <Setter.Value>
                                <Grid>
                                    <Rectangle Width="Auto" Height="Auto" Tag="None" RadiusY="18" RadiusX="18">
                                        <Rectangle.Fill>
                                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                <GradientStop Color="#FFBBBBBB" Offset="0.004"/>
                                                <GradientStop Color="#FFBBBBBB" Offset="0.992"/>
                                                <GradientStop Color="#FFEEEEEE" Offset="0.209"/>
                                                <GradientStop Color="#FFEAEAEA" Offset="0.771"/>
                                                <GradientStop Color="White" Offset="0.478"/>
                                            </LinearGradientBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                </Grid>
    
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding ButtonState, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="On">
                        <Setter Property="Content" Value="Image2"/>
                        <Setter Property="Content">
                            <Setter.Value>
                                <Grid>
                                    <Rectangle Width="Auto" Height="Auto" Tag="None" RadiusY="18" RadiusX="18">
                                        <Rectangle.Fill>
                                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                <GradientStop Color="#FF2A9C25" Offset="0.004"/>
                                                <GradientStop Color="#FF2A9C25" Offset="0.992"/>
                                                <GradientStop Color="#FF8DE28D" Offset="0.209"/>
                                                <GradientStop Color="#FFA3E6A0" Offset="0.763"/>
                                                <GradientStop Color="#FFC9FFC6" Offset="0.482"/>
                                            </LinearGradientBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                </Grid>
    
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding ButtonState, RelativeSource={RelativeSource AncestorType=UserControl}}" Value="Off">
                        <Setter Property="Content" Value="Image3"/>
                        <Setter Property="Content">
                            <Setter.Value>
                                <Grid>
                                    <Rectangle Width="Auto" Height="Auto" Tag="None" RadiusY="18" RadiusX="18">
                                        <Rectangle.Fill>
                                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                <GradientStop Color="#FF9C2525" Offset="0.004"/>
                                                <GradientStop Color="#FF9C2525" Offset="0.992"/>
                                                <GradientStop Color="#FFE28D8D" Offset="0.209"/>
                                                <GradientStop Color="#FFE6A0A0" Offset="0.763"/>
                                                <GradientStop Color="#FFFFC6C6" Offset="0.482"/>
                                            </LinearGradientBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                </Grid>
    
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
        <Button.Template>
            <ControlTemplate TargetType="{x:Type Button}">
                <Grid>
                    <Rectangle Width="50" Height="50" Tag="None" RadiusY="18" RadiusX="18">
                        <Rectangle.Fill>
                            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                <GradientStop Color="#FFBBBBBB" Offset="0.004"/>
                                <GradientStop Color="#FFBBBBBB" Offset="0.992"/>
                                <GradientStop Color="#FFEEEEEE" Offset="0.209"/>
                                <GradientStop Color="#FFEAEAEA" Offset="0.771"/>
                                <GradientStop Color="White" Offset="0.478"/>
                            </LinearGradientBrush>
                        </Rectangle.Fill>
                    </Rectangle>
                    <ContentPresenter Content="{TemplateBinding Content}"/>
                </Grid>
            </ControlTemplate>
        </Button.Template>
    </Button>