WPF ContextMenu IsChecked Binding - wpf

How to write Binding for "IsChecked" ?
I want to check either of 2 menu.
If I click "Hello1", I want to add check on "Hello1",
and remove Check of "Hello2".
If I click "Hello2", I want to add check on "Hello2",
and remove Check of "Hello1".
I tried for 1 week. but I can not. Please help me "How to write binding"
There is no example for internet.
I recently started WPF programming. I only can do form application.
it is very big different....
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ContextMenu Name="ContextMenuMain" x:Key="menuListBox">
<MenuItem Header="Hello1" IsChecked="{Binding Path=IsCheck1, Mode=OneWay}" Name="ContextMenu_Hello1" Click="ContextMenuClick_Hello1"/>
<MenuItem Header="Hello2" IsChecked="{Binding Path=IsCheck2, Mode=OneWay}" Name="ContextMenu_Hello2" Click="ContextMenuClick_Hello2"/>
</ContextMenu>
</Window.Resources>
</Window>
My most difficult point is below code.
I tried to use name of "ContextMenu_Hello1" in MainWindow.
but C# does not allow me to use it...
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ContextMenu_Hello1.DataContext // error !
}
}
Is this out of scope ? but Why ?

Set the ContextMenu property of the window. You should also set the IsCheckable property to true if you want to be able to check the menu items. Also, you cannot set the binding mode to OneWay:
<Window x:Class="WpfApp2.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:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.ContextMenu>
<ContextMenu Name="ContextMenuMain">
<MenuItem Header="Hello1" IsCheckable="True" IsChecked="{Binding Path=IsCheck1}" Name="ContextMenu_Hello1" />
<MenuItem Header="Hello2" IsCheckable="True" IsChecked="{Binding Path=IsCheck2}" Name="ContextMenu_Hello2" />
</ContextMenu>
</Window.ContextMenu>
</Window>
When you have made these changes, you can then bind to any property of the DataContext:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow ()
{
InitializeComponent();
DataContext = this;
}
private bool _isCheck1;
public bool IsCheck1
{
get { return _isCheck1; }
set
{
_isCheck1 = value;
NotifyPropertyChanged();
_isCheck2 = !_isCheck1;
NotifyPropertyChanged("IsCheck2");
}
}
private bool _isCheck2;
public bool IsCheck2
{
get { return _isCheck2; }
set
{
_isCheck2 = value;
NotifyPropertyChanged();
_isCheck1 = !_isCheck2;
NotifyPropertyChanged("IsCheck1");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

Related

WPF Binding not refreshing if Equals and GetHashCode [duplicate]

I have a project, where I bind a checkbox's IsChecked property with a get/set in the codebehind. However, when the application loads, it doesn't update, for some reason. Intrigued, I stripped it down to its basics, like this:
//using statements
namespace NS
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool _test;
public bool Test
{
get { Console.WriteLine("Accessed!"); return _test; }
set { Console.WriteLine("Changed!"); _test = value; }
}
public MainWindow()
{
InitializeComponent();
Test = true;
}
}
}
XAML:
<Window x:Class="TheTestingProject_WPF_.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" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Viewbox>
<CheckBox IsChecked="{Binding Path=Test, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Viewbox>
</Grid>
And, lo and behold, when I set it to true, it did not update!
Anyone can come up with a fix, or explain why?
Thanks, it'd be appreciated.
In order to support data binding, your data object must implement INotifyPropertyChanged
Also, it's always a good idea to Separate Data from Presentation
public class ViewModel: INotifyPropertyChanged
{
private bool _test;
public bool Test
{ get { return _test; }
set
{
_test = value;
NotifyPropertyChanged("Test");
}
}
public PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
<Window x:Class="TheTestingProject_WPF_.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>
<Viewbox>
<CheckBox IsChecked="{Binding Path=Test, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Viewbox>
</Grid>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel{Test = true};
}
}

Dynamic Validation In WPF Using MVVM?

How to do text box input validation in wpf MVVM structure?
You should implement the INotifyDataErrorInfo interface in your view model:
public class ViewModel : INotifyDataErrorInfo
{
private readonly Dictionary<string, string> _validationErrors = new Dictionary<string, string>();
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
//validate:
if (_text?.Length < 3)
_validationErrors[nameof(Text)] = "Too short...";
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(nameof(Text)));
}
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public bool HasErrors => _validationErrors.Count > 0;
public IEnumerable GetErrors(string propertyName) =>
_validationErrors.TryGetValue(propertyName, out string error) ? new string[1] { error } : null;
}
View:
<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="300" Width="300">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Window>
check this: https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-implement-binding-validation and consider on this: Validation.ErrorTemplate="{StaticResource validationTemplate}"

WPF XAML - Property update in nested controls

I have a control that exposes a string property named HeaderText in this way:
public partial class HeaderControl : UserControl
{
public static DependencyProperty HeaderTextProperty;
[Category("Header Properties")]
public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set { SetValue(HeaderTextProperty, value); }
}
static HeaderControl()
{
HeaderTextProperty = DependencyProperty.Register("HeaderText", typeof(string), typeof(HeaderControl), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}
public HeaderControl()
{
this.DataContext = this;
InitializeComponent();
}
}
HeaderControl's Xaml:
<UserControl x:Class="Col.HMI.Controls.HeaderControl"
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">
<Border Background="{Binding Path=HeaderBackground}" >
<TextBlock Text="{Binding Path=HeaderText}" Foreground="White" TextAlignment="Left" VerticalAlignment="Center" HorizontalAlignment="Stretch" FontFamily="Segoe UI Light" FontSize="36" Margin="5"/>
</Border>
and I want to use this HeaderControl in another UserControl, in this way:
OtherControl's Xaml:
<controls:HeaderControl Grid.Row="0" HeaderText="DEMO" />
And this works without problems. But if I bind the HeaderText property to a string property in the OtherControl ViewModel, in this way:
<controls:HeaderControl Grid.Row="0" HeaderText="{Binding Path=SummaryTitle}" />
the bind doesn't work.
This is the SummaryTitle property in the OtherControl ViewModel:
public string SummaryTitle
{
get
{
return _summaryTitle;
}
set
{
_summaryTitle = value; OnPropertyChanged("SummaryTitle");
}
}
PS: I have other controls binded to the OtherControl View Model and they work well.
You are setting DataContext of HeaderControl to itself in the constructor by doing this:
this.DataContext = this;
That means, when you apply some binding to any of the properties in HeaderControl, the Binding engine tries to find the bound property (in your case SummaryTitle) in this control, which it wont find and will fail.
So, to fix your problem, do not set the DataContext of HeaderControl to itself in the Constructor and the Binding engine will try find the properties in the correct DataContext.
Update your HeaderControl constructor to the following, and the bindings should start to work:
public HeaderControl()
{
InitializeComponent();
}
UPDATE
What you are trying to do here is, You want to have DependencyProperty named HeaderText in your UserControl, so that it's value can be set via DataBinding, and then update a value of TextBlock in your UserControl with the value of that DependencyProperty.
You can achieve this by two ways:
1) By updating TextBlock Binding to use ElementNme and Path syntax, XAML would look like this:
<UserControl x:Class="WpfApplication1.HeaderControl"
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"
x:Name="_This">
<Grid>
<TextBlock Text="{Binding ElementName=_This, Path=HeaderText}" FontSize="24" />
</Grid>
</UserControl>
With this approach, whenever the property HeaderText is changed either via Binding or explicitly setting the value.
2) By listening to property value changed event for HeaderText property and then updating the TextBlock accordingly.
For this approach your HeaderControl.xaml would look like:
<UserControl x:Class="WpfApplication1.HeaderControl"
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>
<TextBlock x:Name="TextBlockInUserControl"/>
</Grid>
</UserControl>
and the HeaderControl.xaml.cs
public partial class HeaderControl : UserControl
{
public static readonly DependencyProperty HeaderTextProperty;
[Category("Header Properties")]
public string HeaderText
{
get { return (string)GetValue(HeaderTextProperty); }
set { SetValue(HeaderTextProperty, value); }
}
static HeaderControl()
{
HeaderTextProperty = DependencyProperty.Register("HeaderText", typeof (string), typeof (HeaderControl),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnHeaderTextPropertyChanged));
}
private static void OnHeaderTextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var headerControl = (HeaderControl) dependencyObject;
headerControl.UpdateTextBlock((string) dependencyPropertyChangedEventArgs.NewValue);
}
void UpdateTextBlock(string text)
{
TextBlockInUserControl.Text = text;
}
public HeaderControl()
{
InitializeComponent();
}
}

Why my binding don't work on a ObservableCollection

Hello I am trying to bind a ItemsSource to a ObservableCollection. It seems the ObservableCollection is not seen by the IntelliSense event if the ObservableCollection is public.
Do I have the declare something in XAML to make it visible ? Like in Window.Ressources.
My XAML code
<Window x:Class="ItemsContainer.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">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding StringList}" />
</StackPanel> </Window>
My C# code
using System.Collections.ObjectModel;
using System.Windows;
namespace ItemsContainer
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<string> stringList = new ObservableCollection<string>();
public ObservableCollection<string> StringList
{
get
{
return this.stringList;
}
set
{
this.stringList = value;
}
}
public MainWindow()
{
InitializeComponent();
this.stringList.Add("One");
this.stringList.Add("Two");
this.stringList.Add("Three");
this.stringList.Add("Four");
this.stringList.Add("Five");
this.stringList.Add("Six");
}
}
}
To the best of my knowledge that binding should bind to the property StringList of the current
DataContext, which is MainWindow.
Thanks for any pointer.
Edit:
This worked for me in XAML
<Window x:Class="ItemsContainer.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">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=StringList}" />
</StackPanel>
</Window>
The DataContext does not default to the MainWindow, you'd have to explicitly set that. Like so:
public MainWindow() {
InitializeComponent();
this.stringList.Add("One");
this.stringList.Add("Two");
this.stringList.Add("Three");
this.stringList.Add("Four");
this.stringList.Add("Five");
this.stringList.Add("Six");
this.DataContext = this;
}

binding to a usercontrol not working

i have encountered a databinding probleme,
so i create a usercontrole
UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static DependencyProperty TestThisProperty = DependencyProperty.Register
(
"TestThis",
typeof(string),
typeof(UserControl1),
new PropertyMetadata("Some Data",new PropertyChangedCallback(textChangedCallBack))
);
public string TestThis
{
get { return (string)GetValue(TestThisProperty); }
set { SetValue(TestThisProperty, value); }
}
static void textChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
UserControl1 _us = (UserControl1)property;
_us.MyUserControl.TestThis = (string)args.NewValue;
}
}
UserControl1.xaml
<UserControl x:Class="WpfApplication1.UserControl1"
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" x:Name="MyUserControl"
d:DesignHeight="300" d:DesignWidth="300">
</UserControl>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
UserControl1 _uc = new UserControl1();
public MainWindow()
{
InitializeComponent();
DispatcherTimer _dt = new DispatcherTimer();
_dt.Interval = new TimeSpan(0, 0, 1);
_dt.Start();
_dt.Tick += new EventHandler(_dt_Tick);
}
private void _dt_Tick(object s,EventArgs e)
{
_uc.TestThis = DateTime.Now.ToString("hh:mm:ss");
}
and finaly the MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Text="{Binding Path=TestThis,ElementName=MyUserControl, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
but the problem here, whene i debug my code i get this warning
Cannot find source for binding with reference 'ElementName=MyUserControl'. and the ui does not update of course,any idea please?
ElementName only targets names in the same namescope, e.g.
<TextBox Name="tb"/>
<Button Content="{Binding Text, ElementName=tb}"/>
If you do not define the UserControl in the MainWindow XAML and give it a name there or register a name in code behind (and target that) you won't get this ElementName binding to work.
An alternative would be exposing the usercontrol as a property on the MainWindow, e.g.
public UserControl1 UC { get { return _uc; } }
Then you can bind like this:
{Binding UC.TestThis, RelativeSource={RelativeSource AncestorType=Window}}

Resources