GcH
GcH

Reputation: 25

WPF Binding MainWindow Control from UserControl, PropertyChanged not fired

I'm new to WPF and facing an issue regarding binding. I have a MainWindow with a Rectangle that uses binding to change visual attributes. The XAML code is as follows:

<Rectangle Height="72" Canvas.Left="1011" RadiusY="6" RadiusX="6" StrokeThickness="2" Width="82" Canvas.Top="8">
    <Rectangle.Style>
        <Style TargetType="Rectangle">
            <Style.Triggers>
                <DataTrigger Binding="{Binding BddState}" Value="True">
                    <Setter Property="Fill">
                      <Setter.Value>
                        <ImageBrush ImageSource="Pictures/BDD_on.png" Stretch="Uniform" />
                      </Setter.Value>
                    </Setter>

                </DataTrigger>
                <DataTrigger Binding="{Binding BddState}" Value="False">
                    <Setter Property="Fill">
                        <Setter.Value>
                            <ImageBrush ImageSource="Pictures/Bdd_off.png" Stretch="Uniform" />
                        </Setter.Value>
                    </Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Rectangle.Style>
</Rectangle>

I created a view model class to manage the binding:

public class ViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(String propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }


    private Boolean _bddstate;
    public Boolean BddState
    {
        get { return _bddstate; }
        set
        {
            _bddstate = value;
            OnPropertyChanged("BddState");
        }
    }
}

The binding works well from MainWindow using:

private ViewModel _viewModel = new ViewModel();

public MainWindow()
{
    InitializeComponent();
    this.DataContext = _viewModel;
    _viewModel.BddState = true;        
}

Inside the MainWindow, I use a StackPanel to load different UserControl (the MainWindow acts as an header). My concern is the binding from the UserControl; the BddState value changes accordingly but nothing is reflected on MainWindow UI. At the same time, I can see that the handler of the view model is always null.

What am I doing wrong?

Thanks in advance for your support.

Update: Here is the code of the user control:

<Grid Background="#FFECECEC" Height="706" VerticalAlignment="Top">
    <Label x:Name="label" Content="HOME" HorizontalAlignment="Left" Margin="525,8,0,0" VerticalAlignment="Top" FontWeight="Bold" Foreground="{DynamicResource gray_2}" FontSize="20"/>
    <Label x:Name="label_Copy" Content="General Info" HorizontalAlignment="Left" Margin="441,34,0,0" VerticalAlignment="Top" Foreground="{DynamicResource gray_2}" FontSize="18"/>
    <Button x:Name="button" Content="Button" HorizontalAlignment="Left" Margin="441,214,0,0" VerticalAlignment="Top" Width="107" Height="36" Click="button_Click"/>


</Grid>

for the code behind, it's just the following:

 public partial class Home : UserControl
{
    public Home()
    {
        InitializeComponent();
    }

    private void button_Click(object sender, RoutedEventArgs e)
    {
        ViewModel _vm = new ViewModel();
        _vm.BddState = true;
    }
}

When i click the button of Home UC, the handler of ViewModel is null and the binding is not effective on MainWindow rectangle

here some update following Andy's sample (it could help) by casting mainwindow datacontext it works:

 MainWindowViewModel viewModel = App.Current.MainWindow.DataContext as MainWindowViewModel;
        if (viewModel != null)
        {
            viewModel.BddState = true;
        }

Upvotes: 0

Views: 266

Answers (1)

Andy
Andy

Reputation: 12276

I modified your markup to:

    <Canvas Name="MyCanvas" >
        <Rectangle Height="72"  RadiusY="6" RadiusX="6" StrokeThickness="2" Width="82" Canvas.Top="8">
            <Rectangle.Style>
                <Style TargetType="Rectangle">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding BddState}" Value="True">
                            <Setter Property="Fill" Value="Green"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding BddState}" Value="False">
                            <Setter Property="Fill" Value="Yellow"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Rectangle.Style>
        </Rectangle>
    </Canvas>

Which has no reliance on any image and no Canvas.Left.

I get a green rectangle.

Possible reasons you see nothing include:

Your window is narrower than 1011px and Canvas.Left="1011" means your rectangle is not within your window.

Your image paths are not working.

Uniform stretch is creating a problem and the part of the rectangle which has any picture in it is off screen.

I then modified this further to set bddstate true initially via the private backing field in the viewmodel. Added a togglebutton with ischecked bound to bddstate. That toggles between yellow and green successfully.

Here's a working version involves a usercontrol. I've inherited MainWindowViewModel from a BaseViewModel I had in my test sample.

MainWindow:

 <Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
    <Canvas Name="MyCanvas" >
        <Rectangle Height="72"  RadiusY="6" RadiusX="6" StrokeThickness="2" Width="82" Canvas.Top="8">
            <Rectangle.Style>
                <Style TargetType="Rectangle">
                    <Setter Property="Fill" Value="Green"/>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding BddState}" Value="False">
                            <Setter Property="Fill" Value="Yellow"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Rectangle.Style>
        </Rectangle>
    </Canvas>
    <local:UserControl1 Height="30" Width="100"/>
</Grid>

The viewmodel:

public class MainWindowViewModel : BaseViewModel
{
    private Boolean _bddstate = true;
    public Boolean BddState
    {
        get { return _bddstate; }
        set
        {
            _bddstate = value;
            RaisePropertyChanged("BddState");
        }
    }

UserControl1

    <ToggleButton Width="100" Height="30" Content="Toggle" IsChecked="{Binding BddState, Mode=TwoWay}"/>

BaseViewModel

public  class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void RaisePropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

There is no code behind.

And this still works.

Upvotes: 1

Related Questions