Binding doesn't subscribe to PropertyChangedHandler - c#

Alright so I've been on this for ages now and can't see why it doesn't work.
I Have a binding from my UserControl label onto a property in the class cell. This class implements the interface INotifyPropertyChanged. There everytime the set method is called it will call OnPropertyChanged("value)which is expected to update the label that it's binded to. However this is not the case.
I did some research on stack to clarify some of the problems i've checked:
I do have my datacontext set since I use mvvm light it is set to the viewmodel locator.
this property is outside of the viewmodel but it's suppose to be in order to not make it more expandable towards other sizes of sudoku.
Edit: The Property changed is fired however the handler is always null I suspect it's not being subscribed to, hence the title.
Edit 2 The code is Sudoku game which is basically a grid of 3x3 with grids of 3x3 The hiarchy here would be Outtergrid has innergrids and innergrids have cells I thought it would be better not to include these codes since they are basically the same.
Snipit of the datacontext declared MainWindow.Window:
<Window x:Class="SudokuWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SudokuWPF"
DataContext="{Binding Main, Source={StaticResource Locator}}"
Title="SudokuWindow" Height="350" Width="525">
here is the code of the Cell class
public class Cell : INotifyPropertyChanged
{
private int _value;
public int Value
{
get { return _value; }
set
{
_value = value;
//Content = _value;
OnPropertyChanged("Value");
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And the binding in the usercontrol
<UserControl x:Class="SudokuWPF.UserControlSudoku"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:LocalControl="clr-namespace:SudokuWPF"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
HorizontalAlignment ="Stretch"
HorizontalContentAlignment ="Stretch"
VerticalAlignment ="Stretch"
VerticalContentAlignment ="Stretch"
Foreground="White"
Width="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}">
<UserControl.Resources>
<LocalControl:Cell x:Key="Cell"></LocalControl:Cell>
<LocalControl:Innergrid x:Key="Innergrid"></LocalControl:Innergrid>
<LocalControl:OuterGrid x:Key="OuterGrid"></LocalControl:OuterGrid>
<DataTemplate x:Key="CellTemplate">
<Border x:Name="Border" BorderBrush="AliceBlue" BorderThickness="1">
<Label Content="{Binding Source={StaticResource Cell}, Path=Value, UpdateSourceTrigger=PropertyChanged}"></Label>
</Border>
</DataTemplate>
Any help would be greatly appreciated.

Alright so after wrongfully eliminating the datacontext out of the problem it turned out this was the core of the problem. I now binded the datacontext to my outer grid object like this:
<UserControl x:Class="SudokuWPF.UserControlSudoku"
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" mc:Ignorable="d"
HorizontalAlignment ="Stretch"
HorizontalContentAlignment ="Stretch"
VerticalAlignment ="Stretch"
VerticalContentAlignment ="Stretch"
Foreground="White"
Width="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}"
DataContext="{Binding Source=OuterGrid}"
>
public OuterGrid outerGrid = new OuterGrid();
public UserControlSudoku()
{
InitializeComponent();
MainList.DataContext = outerGrid;
}

Related

Why WPF RaisePropertyChanged can't update reference type binding?

I use one window to change the data, while using another window(MainWindow) to show the data.
Unexpectedly, when MainWindowViewModel catches the PropertyChanged event and RaisePropertyChanged to update MainWindow view, nothing happened in the view.
In the debugger, I found the MainWindowViewModel property has changed,and Debug has printed the message, but view not change.
I'm using Mvvmlight.
Sorry for my poor English.
I'd appreciate it if you could help me. XD!
Here is the View code:
<Window x:Class="OneTimetablePlus.Views.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:OneTimetablePlus.Views"
xmlns:tb="http://www.hardcodet.net/taskbar"
mc:Ignorable="d"
Title="MainWindow" Height="730" Width="91.52"
AllowsTransparency="True"
WindowStyle="None"
Background="Transparent"
ShowInTaskbar="False"
Topmost="True"
ResizeMode="NoResize"
DataContext="{Binding Main, Source={StaticResource Locator}}"
>
<ListBox ItemsSource="{Binding TodayDayCourses}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ShowName}" Style="{StaticResource LargeText}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
Here is the ViewModel code:
public class MainViewModel : ViewModelBase
{
public MainViewModel(IDataProvider dataProvider)
{
DataProvider = dataProvider;
dataProvider.PropertyChanged += (sender, e) =>
{
if (e.PropertyName == GetPropertyName(() => dataProvider.TodayDayCourse))
{
Debug.Print("Catch PropertyChanged TodayDayCourse");
RaisePropertyChanged(() => TodayDayCourses);
}
};
}
public List<Course> TodayDayCourses => DataProvider.TodayDayCourse2;
public IDataProvider DataProvider { get; }
}
As Clemens said, we should use => DataProvider.TodayDayCourse2.ToList() instead of => DataProvider.TodayDayCourse2.
Because the latter always returns the same instance, while is not a property changed.

Visibility of one user control to another user control

private void Button_Click(object sender, RoutedEventArgs e)
{
int selectedValue = (int)comboSelection.SelectedValue;
if (selectedValue == 8)
{
EightTiles et = new EightTiles();
this.Visibility = Visibility.Collapsed;
et.Visibility = Visibility.Visible;
}
}
My target is when the combo box selection is equal to 8 then click the button, current usercontrol get collapsed and next usercontrol(EightTiles) get visible.
But my problem is when i click the button it shows a blank page, next user control page doesn't showed, what is the problem and how i do to solve it..
Thanks
Plase both these controls as content of a main content control to xaml, and manage the second control visibility based on trigger of the first control. Here what I can suggest you:
1. XAML:
<Window x:Class="SoDataGridProjectsHelpAttempt.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:soDataGridProjectsHelpAttempt="clr-namespace:SoDataGridProjectsHelpAttempt"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ContentControl >
<ContentControl.ContentTemplate>
<DataTemplate>
<Grid>
<soDataGridProjectsHelpAttempt:MainSubControl x:Name="MainSubControl" Visibility="Visible"/>
<soDataGridProjectsHelpAttempt:SubSubControl x:Name="SubSubControl" Visibility="Collapsed"/>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="Control.Visibility" Value="Collapsed" SourceName="MainSubControl">
<Setter TargetName="SubSubControl" Property="Visibility" Value="Visible"></Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
2. MainSubControl:
<UserControl x:Class="SoDataGridProjectsHelpAttempt.MainSubControl"
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:soDataGridProjectsHelpAttempt="clr-namespace:SoDataGridProjectsHelpAttempt"
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:system="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" Visibility="{Binding IsControlVisible, Converter={StaticResource Bol2VisibilityConverter}, UpdateSourceTrigger=PropertyChanged}">
<UserControl.Resources>
<x:Array Type="system:Int32" x:Key="DecimalsArray">
<system:Int32>7</system:Int32>
<system:Int32>5</system:Int32>
<system:Int32>3</system:Int32>
<system:Int32>8</system:Int32>
</x:Array>
</UserControl.Resources>
<UserControl.DataContext>
<soDataGridProjectsHelpAttempt:MainSubViewModel/>
</UserControl.DataContext>
<StackPanel>
<ComboBox ItemsSource="{StaticResource DecimalsArray}"
Width="Auto"
SelectedItem="{Binding SelectedComboItem}"/>
<Button Command="{Binding Command}">Press me!!!</Button>
</StackPanel>
3. MainSubControl ViewModel:
public class MainSubViewModel : BaseObservableObject
{
private int _selectedComboItem;
private ICommand _command;
private bool _isControlVisible;
public MainSubViewModel()
{
IsControlVisible = true;
}
public ICommand Command
{
get { return _command ?? (_command = new RelayCommand(CommandMethod)); }
}
private void CommandMethod()
{
if (SelectedComboItem == 8)
IsControlVisible = false;
}
public bool IsControlVisible
{
get { return _isControlVisible; }
set
{
_isControlVisible = value;
OnPropertyChanged();
}
}
public int SelectedComboItem
{
get { return _selectedComboItem; }
set
{
_selectedComboItem = value;
OnPropertyChanged();
}
}
}
4. SecondSubControl:
<UserControl x:Class="SoDataGridProjectsHelpAttempt.SubSubControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Image Source="2014_8_27_Bing_en-AU.jpg" Margin="50"></Image>
</Grid>
5. App.xaml (put into Application.Resources):
<BooleanToVisibilityConverter x:Key="Bol2VisibilityConverter" />
regards,
your problem is that et goes out of scope, so after the function is doesn't exist anymore. you need to create it "outside" in a scope where it will still exist before and after that function.
So for example if there's a class called Application and that class holds the current control that your Button_click is attached to, you need to create the other control there too. Or at least the et variable if you don't want to create the EightTiles until you need it.

TabControl - Share Main ViewModel

I want to be able to share controls between my main window and tab controls. Right now, I have a tab control and status bar on the main window. Status bar contains a button and progress bar. I want to share the progress bar. When the button is clicked, it resets progress bar.
The tabs contain a button. When clicked, it should increase the progress bar by 1.
What I've done: after digging through SO/other all day, I was able to set this up.
MainWindowView
-> Declares instance of MainWindowViewModel in xaml
-> Declares tabs as usercontrols in xaml b/c will always have the same tabs
TabControl1View
-> Declares instance of TabControl2ViewModel in xaml
I need advice on how to talk to MainWindowViewModel in TabControl1View.
<Window x:Class="TabControlTest.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabControlTest"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<DockPanel>
<StatusBar DockPanel.Dock="Bottom">
<StatusBarItem HorizontalContentAlignment="Stretch" DockPanel.Dock="Top">
<Button Height="25" Content="Reset" Command="{Binding MainWindowViewModel_ButtonClickCommand}"/>
</StatusBarItem>
<StatusBarItem HorizontalContentAlignment="Stretch" DockPanel.Dock="Bottom">
<ProgressBar Height="25" Maximum="10" Value="{Binding MainWindowViewModel_Progress, Mode=TwoWay}"/>
</StatusBarItem>
</StatusBar>
<TabControl>
<TabItem>
<local:TabControl1View/>
</TabItem>
</TabControl>
</DockPanel>
class MainWindowViewModel : ViewModelBase
{
int progress = 0;
public int MainWindowViewModel_Progress
{
get
{
return progress;
}
set
{
SetAndNotify(ref this.progress, value, () => this.MainWindowViewModel_Progress);
}
}
ICommand _ButtonClickCommand;
public ICommand MainWindowViewModel_ButtonClickCommand
{
get
{
return _ButtonClickCommand ?? (_ButtonClickCommand = new CommandHandler(() => MainWindowViewModel_ButtonClick(), true));
}
}
public void MainWindowViewModel_ButtonClick()
{
MainWindowViewModel_Progress = 0; // Reset progress
}
}
x
<UserControl x:Class="TabControlTest.TabControl1View"
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:TabControlTest"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.DataContext>
<local:TabControl1ViewModel/>
</UserControl.DataContext>
<Grid>
<Button Content="UP" Command="{Binding TabControl1ViewModel_UpClickCommand}"/>
</Grid>
class TabControl1ViewModel : ViewModelBase
{
ICommand _UpClickCommand;
public ICommand TabControl1ViewModel_UpClickCommand
{
get
{
return _UpClickCommand ?? (_UpClickCommand = new CommandHandler(() => TabControl1ViewModel_UpClick(), true));
}
}
public void TabControl1ViewModel_UpClick()
{
// I want to increase the progress bar here
}
}
I found a good answer on using a mediator pattern here:
How can I update a property of mainWindowViewModel from another ViewModel?
But I don't understand how I can store a reference to TabControl1ViewModel in MainWindowViewModel.
Also found a ton of articles saying to declare the ViewModels inside MainViewModel and keep track of them, but they were always code snippets.
Question: how do I pass MainViewModel.Progress to TabControlViewModel?
I'm now going down the path of trying to put something like this in my MainViewModel
MainViewModel.ProgressChanged = TabControlView.(get TabControlViewModel).ProgressChanged
When you do this
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
or
<UserControl.DataContext>
<local:TabControl1ViewModel/>
</UserControl.DataContext>
You are letting the View to create a instance of MainWindowViewModel or TabControl1ViewModel by itself and bind it to your View's datacontext. That makes you loose control of passing any constructor parameter to the control.
In your MainWindowViewModel's constructor initialize the User control's Datacontext like
class MainWindowViewModel
{
MainWindowViewModel
{
ChildViewModel = new TabControl1ViewModel(this);
}
public TabControl1ViewModel ChildViewModel {get; private set;}
}
class TabControl1ViewModel
{
public MainWindowViewModel ParentViewModel {get; private set;}
TabControl1ViewModel(MainWindowViewModel mainWindowViewModel)
{
ParentViewModel = mainWindowViewModel;
}
}
View.Xaml
<Window x:Class="TabControlTest.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabControlTest"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<DockPanel>
.
.
.
<TabControl>
<TabItem>
<local:TabControl1View DataContext={Binding ChildViewModel}/>
</TabItem>
</TabControl>
</DockPanel>

Binding with a single Window Class

This is out of curiousity question. I know you should not structure any real WPF applications this way.
Working within and using only the MainWindow Class how do you bind an XAML element to a CLR property?
Here is my XAML.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBlock DockPanel.Dock ="Top" Height="50" Width="50"
Background ="AliceBlue" FontSize ="16" />
</Grid>
</Window>
And Code
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
string _myString = "hello world";
public string MyString
{
get { return _myString; }
}
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
}
So I want to display the string in the textblock. I think its a simple binding but I can't find the right syntax. Thanks
You must set your window data context to itself.
<Window.DataContext>
<Binding RelativeSource="{RelativeSource Self}"/>
</Window.DataContext>
then you cant bind to your property MyString
<TextBlock DockPanel.Dock ="Top" Height="50" Width="50"
Background ="AliceBlue" FontSize ="16" />
In this case you can simply do:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
x:Name="MainWindow">
<Grid>
<TextBlock DockPanel.Dock ="Top" Height="50" Width="50"
Background ="AliceBlue" FontSize ="16"
Text="{Binding MyString, ElementName=MainWindow}" />
</Grid>
</Window>

Bind Visibility to different source than DataContext

I have two ViewModels:
public class CommandViewModel
{
public string DisplayName { get; set; }
public ICommand Command { get; set; }
}
and
public class SomeViewModel : INotifyPropertyChanged
{
private bool someFlag;
private CommandViewModel someCommand;
public bool SomeFlag
{
get
{
return someFlag;
}
set
{
if (value == someFlag)
return;
someFlag = value;
OnPropertyChanged("SomeFlag");
}
}
public CommandViewModel SomeCommandViewModel
{
get
{
if (someCommand == null)
{
someCommand = new CommandViewModel();
// TODO: actually set the DisplayName and Command
}
return someCommand;
}
}
}
And I have two corresponding Views:
<UserControl x:Class="ButtonView"
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"
mc:Ignorable="d"
d:DesignHeight="28" d:DesignWidth="91">
<Button Content="{Binding Path=DisplayName}" Command="{Binding Path=Command}" />
</UserControl>
and
<UserControl x:Class="SomeView"
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"
mc:Ignorable="d" Height="125" Width="293" />
<ViewButton
Visibility="{Binding Path=SomeFlag, Converter={StaticResource BoolToVisibilityConverter}}"
DataContext="{Binding Path=SomeCommandViewModel}" />
</UserControl>
I'm having a problem getting ButtonView's Visibility bound when its DataContext is also bound. If I leave the DataContext out, the Visibility works just fine (when SomeFlag switches value, the button's visibility changes with it) - but the display text and command don't work. If I bind the DataContext, the display text and command work, but the visibility doesn't. I'm sure it has to do with the fact that when I bind the DataContext to SomeCommandViewModel, it is expecting "SomeFlag" to exist within it. And of course, it doesn't.
If you set the DataContext of any given Element EVERY Binding (including children ones) of this element will use the DataContext as Source unless you explicitly give another source.
What you seem to do is specify 2 DataContext at once (UserControl.DataContext is NOT read as ViewButton.DataContext is set and the first source it finds counts).
You can either explicitly take the datacontext of a given element as Kent states
OR
you can specify the source explicitly.
e.g.
<ViewButton
Visibility="{Binding Path=SomeFlag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Converter={StaticResource BoolToVisibilityConverter}}"
DataContext="{Binding Path=SomeCommandViewModel}" />
I don't condone your design, but this will work around your immediate problem:
<UserControl x:Name="root"
x:Class="SomeView"
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"
mc:Ignorable="d" Height="125" Width="293" />
<ViewButton
Visibility="{Binding Path=DataContext.SomeFlag, Converter={StaticResource BoolToVisibilityConverter}, ElementName=root}"
DataContext="{Binding Path=SomeCommandViewModel}" />
</UserControl>

Categories

Resources