Wpf Binding Bridge - c#

I have a feeling this is a bug in wpf. Let me know what you guys think of this.
To keep everything simple I made demo example in .net 4.0
I have a ContentControl with Content bound to Data and ContentTemplate which holds a CheckBox bound to Content.
The problem is Ok property is never true no matter how often I click the CheckBox.
As if CheckBox doesn't pass the new value to ViewModel.
Take a look at this:
<Window.Resources>
<DataTemplate x:Key="dataTemplate">
<CheckBox IsChecked="{Binding Path=., Mode=TwoWay}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding Path=Ok, Mode=TwoWay}" ContentTemplate="{StaticResource dataTemplate}"/>
</Grid>
This is my ViewModel
public MainWindow()
{
InitializeComponent();
ViewModel vm = new ViewModel();
this.DataContext = vm;
}
public class ViewModel : INotifyPropertyChanged
{
private string txt;
public string Txt
{
get { return txt; }
set { txt = value; this.OnPropertyChanged("Txt"); }
}
private bool ok;
public bool Ok
{
get { return ok; }
set { ok = value; this.OnPropertyChanged("Ok");}
}
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Any ideas how to fix this issue with ContentTemplate?

Your problem is a common problem related to the use of value types. You're data binding your checkbox to a primitive value type (bool) (which is a pretty uncommon thing to do). Since Binding.Source is of type object, your boolean is getting boxed into an object. Any updates on that boxed object has no effect on the original property on ViewModel.
You can test this theory by replacing that boolean with a struct like this:
public struct MyStruct
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; }
}
public ViewModel Parent { get; set; }
}
and change your binding:
<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"/>
If you put breakpoints on the IsChecked getter and setter, you will see that the binding works. However, when you hit one of the break points, try to investigate this value in the Immediate Window:
? this.Parent.Ok.IsChecked
You should see that the MyStruct property on the parent view model is not being affected by the databinding at all.
Full test code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModel vm = new ViewModel();
vm.Ok = new MyStruct { Parent = vm, IsChecked = false };
this.DataContext = vm;
}
}
public class ViewModel : INotifyPropertyChanged
{
private string txt;
public string Txt
{
get { return txt; }
set { txt = value; this.OnPropertyChanged("Txt"); }
}
private MyStruct ok;
public MyStruct Ok
{
get { return ok; }
set { ok = value; this.OnPropertyChanged("Ok"); }
}
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public struct MyStruct
{
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; }
}
public ViewModel Parent { get; set; }
}
}
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">
<Window.Resources>
<DataTemplate x:Key="dataTemplate">
<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding Path=Ok, Mode=TwoWay}" ContentTemplate="{StaticResource dataTemplate}"/>
</Grid>
</Window>

you can do this way
<Window.Resources>
<DataTemplate x:Key="dataTemplate">
<CheckBox IsChecked="{Binding Path=Ok, Mode=TwoWay}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource dataTemplate}"/>
</Grid>
in above example I bind the Content to the view model itself and then IsChecked of CheckBox to the Ok property.

The Problem is that ContentControl won't pass it's DataContext to the Content.
You need to set the DataContext manually.
<CheckBox DataContext="{Binding DataContext,RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}" IsChecked="{Binding Path=., Mode=TwoWay}"/>

After some more research (and after posting an irrelevant answer and then deleting it again) -- and being motivated by some discussion with Eren Ersönmez and Dev Hedgehog -- the reason of why the two-way binding IsChecked="{Binding Path=., Mode=TwoWay}" cannot work became more clear.
(Side note: This answer does not contain a solution to the problem. Pushpraj's answer already provides a nice solution.)
{Binding Path=., Source=SomeSource} denotes a binding which binds to the binding source directly. An equivalent binding syntax is {Binding Source=SomeSource}.
However, bindings to the binding source itself cannot be two-way. Trying so by specifying {Binding Source=SomeSource, Mode=TwoWay} will cause an InvalidOperationException during run-time.
On the other hand, {Binding Path=., Source=SomeSource, Mode=TwoWay} will not produce an exception - but it still does not enable a two-way binding for such a data binding. Rather, it only fools the binding validation/error handling mechanism of the binding engine.
This has been already found out and discussed in answers to other binding-related questions, such as the answers by Allon Guralnek or by H.B. (and were it not for their answers and my discussion with Eren Ersönmez, i would probably still be clueless of what is going on here...).
This also means that the issue is not caused by a boxed value type being a binding source per se, but rather due to trying to use a two-way binding with the binding path ..
Why does it make no sense to have two-way bindings with binding path .?
Below is a small example demonstrating the issue. It also demonstrates that the problem is not restricted to situations where a boxed value type is being the binding source, nor that it is restricted to bindings involving DataContext.
public class Demo
{
public static string SourceString
{
get { return _ss; }
set { _ss = value; }
}
private static string _ss = "Hello World!";
}
The string (a reference type) provided by static property Demo.SourceString will be used as the binding source in the following XAML snippet:
<TextBox Text="{Binding Source={x:Static My:Demo.SourceString}, Mode=TwoWay}" />
(Note that the object provided by the My:Demo.SourceString property is the binding source; the property itself is not the binding source.)
This XAML above will produce an InvalidOperationException at run-time.
However, using the equivalent XAML:
<TextBox Text="{Binding Path=., Source={x:Static My:Demo.SourceString}, Mode=TwoWay}" />
will not produce an exception during run-time, but the binding is nevertheless not a working two-way binding. Text entered into the text box will not be promoted back to the Demo.SourceString property. It can't -- all the binding knows is that it has a string object as source and a binding path which is ..
...but it would just need to update Demo.SourceString, that can't be too difficult, or?
Assume for a moment that a two-way binding with a path . as shown in the XAML above would try to work as a two-way binding - would it result in meaningful behavior? What would happen when the TextBox.Text property has changed its value due to the user inputting text?
The binding would theoretically try to promote the new string value back to the binding source by applying the binding path . onto the object that serves as the binding source (the string "Hello World!"). That does not make much sense... Well, it could perhaps replace the original binding source (the "Hello World!" string) with the string from the TextBox.Text property - but this would be rather meaningless as this change would be local and internal to the binding only. The binding would not be able to assign the new string to Demo.SourceString -- unless someone knows a way of how to obtain a reference to Demo.SourceString by applying the binding path . to a string object containing "Hello World!". Hence, two-way binding mode is not a feasible option for bindings which bind to the binding source itself.
(If somebody is confused about how the example above would apply to a binding using the DataContext like {Binding Path=., Mode=TwoWay}, just ignore the class Demo and substitute any occurences of Demo.SourceString in the text with DataContext.)

Related

Creating generalized user controls with MVVM Light

How to create a general user control using MVVM Light?
All the main views in the application seem to work fine. However, general controls doesn't seem to accept bindings. This is my FileDiplay control. An icon and a TextBlock displaying a filename next to it.
Utilization
In one of the main views, I try to bind a FileName inside an ItemsTemplate of an ItemsControl. Specifying a literal, like FileName="xxx" works fine, but binding doesn't.
<local:FileLink FileName="{Binding FileName}" />
I've been playing around with DependencyProperty and INotifyPropertyChanged a lot. And seemingly there's no way around a DependencyProperty, since it can't be bound otherwise. When using a simple TextBlock instead of this user control, binding is accepted.
I didn't include the locator or the utilizing control in order to avoid too much code. In fact, I think this is a very simple problem that I haven't found the solution for, yet. I do think that having the DataContext set to the ViewModel is correct, since no list binding or real UserControl separation is possible. I've also debugged into the setters and tried the different approaches.
FileLink.xaml
<local:UserControlBase
x:Class="....FileLink"
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:..."
mc:Ignorable="d" DataContext="{Binding FileLink, Source={StaticResource Locator}}">
<Grid>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon}" Margin="0,0,5,0" />
<TextBlock Text="{Binding FileName}" />
</StackPanel>
</Grid>
</local:UserControlBase>
FileLink.xaml.cs
using System.Windows;
using System.Windows.Media;
namespace ...
{
public partial class FileLink : UserControlBase
{
private FileLinkViewModel ViewModel => DataContext as FileLinkViewModel;
public static DependencyProperty FileNameProperty = DependencyProperty.Register(nameof(FileName), typeof(string), typeof(FileLink));
public ImageSource Icon
{
get
{
return App.GetResource("IconFileTypeCsv.png"); // TODO:...
}
}
public string FileName
{
get
{
return ViewModel.FileName;
}
set
{
ViewModel.FileName = value;
}
}
public FileLink()
{
InitializeComponent();
}
}
}
FileLinkViewModel.cs
using GalaSoft.MvvmLight;
namespace ...
{
public class FileLinkViewModel : ViewModelBase
{
private string _FileName;
public string FileName
{
get
{
return _FileName;
}
set
{
Set(() => FileName, ref _FileName, value);
}
}
}
}
Do not explicitly set the DataContext of your UserControl, because it effectively prevents that the control inherits the DataContext from its parent control, which is what you expect in a Binding like
<local:FileLink FileName="{Binding FileName}" />
Also, do not wrap the view model properties like you did with the FileName property. If the view model has a FileName property, the above binding works out of the box, without any wrapping of the view model.
If you really need a FileName property in the UserControl, it should be a regular dependency property
public partial class FileLink : UserControlBase
{
public FileLink()
{
InitializeComponent();
}
public static readonly DependencyProperty FileNameProperty =
DependencyProperty.Register(nameof(FileName), typeof(string), typeof(FileLink));
public string FileName
{
get { return (string)GetValue(FileNameProperty); }
set { SetValue(FileNameProperty, value); }
}
}
and you should bind to it by specifying the UserControl as RelativeSource:
<local:UserControlBase ...> <!-- no DataContext assignment -->
<StackPanel Orientation="Horizontal">
<Image Source="IconFileTypeCsv.png" Margin="0,0,5,0" />
<TextBlock Text="{Binding FileName,
RelativeSource={RelativeSource AncestorType=UserControl}}" />
</StackPanel>
</local:UserControlBase>

WPF databinding not working despite the target datacontext bound to the correct source memory address

The scenario is very simple here. I'm trying to bind a textbox to a property of a class at runtime:
tb.displayValue.DataContext = p.GetValue(currentNode, null);
xaml for the textbox:
<TextBox Name="displayValue" Grid.Column="1"
Style="{StaticResource propertyTextBoxStyle}"
Text="{Binding Path=DataContext,
RelativeSource={RelativeSource Self},
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}">
</TextBox>
When the application starts, the textbox does get populated with the correct information so the source to target binding is working fine. However, when I try and edit a value in the textbox and then switch focus, the changes are not reflected in the actual data structure. The value would stay on the UI, but as soon as I try to reload the UI from the data structure again it defaults back to the original value.
I suspect the binding is not working correctly at first, but after checking the memory address of tb.displayValue.DataContext and comparing it to the actual memory address of the data structure it's an identical match.
INotifyPropertyChanged has been implemented and I have added the OnPropertyChanged call to every setter. After spending two days trying to debug this issue I think I'm really running out of options here so any suggestion would be appreciated.
The simplest two-way binding works this way: you set the DataContext on your Window to a new instance of your MainWindowViewModel class which implements INotifyPropertyChanged, and you set the binding path on your TextBox to the name of the public property on your ViewModel you want to bind to.
I'm trying to show how you need a public property with a get and set to bind to, and how to properly set the DataContext for your window so that all of the controls within it are able to bind to the public properties available on it.
I've never heard of setting the DataContext of a TextBox directly to the return value from a method before, and it just seems wrong, so maybe you are not going about it the right way, and hopefully this helps you see how it can work.
MainWindow.cs
<Window x:Class="DemoWPFApp1.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:vm="clr-namespace:DemoWPFApp1.ViewModels"
Height="300" Width="460" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<TextBox Name="displayValue" Text="{Binding Path=BoundProperty,
Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
</TextBox>
</Window>
MainWindowViewModel.cs
namespace DemoWPFApp1.ViewModels
{
public class MainWindowViewModel : BaseViewModel
{
private string m_boundProperty;
public string BoundProperty
{
get
{
return m_boundProperty;
}
set
{
m_boundProperty = value; OnPropertyChanged();
}
}
public MainWindowViewModel()
{
BoundProperty = "Some value.";
}
}
}
BaseViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace DemoWPFApp1.ViewModels
{
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propName = null)
{
var e = PropertyChanged;
if (e != null && propName != null)
{
e.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
}
}

UWP Classic Binding not updating UI after setting bound value from Inputcontrol

To simplify my problem, in my app I want to change the user's input to all uppercase. So "foo" should be displayed as "FOO" when the TextBox loses focus.
My Xaml:
<Page x:Class="App12.MainPage"
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:local="using:App12"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.DataContext>
<local:MainViewModel />
</Page.DataContext>
<StackPanel Margin="10,50,10,10" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Text="{Binding Name1, Mode=TwoWay}" />
<TextBox Text="{x:Bind Path=vm.Name2, Mode=TwoWay}" />
<Button HorizontalAlignment="Center">Just a control for the TextBox to lose focus</Button>
</StackPanel>
</Page>
My ViewModel
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace App12
{
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
}
private string _name1 = "something";
public string Name1
{
get
{
return _name1;
}
set
{
_name1 = (string)value.ToUpper();
OnPropertyChanged();
}
}
private string _name2 = "something";
public string Name2
{
get
{
return _name2;
}
set
{
_name2 = (string)value.ToUpper();
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged( [CallerMemberName] string propertyName = null )
{
var handler = PropertyChanged;
handler?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
}
And my code-behind
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace App12
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
MainViewModel vm;
public MainPage()
{
this.InitializeComponent();
DataContextChanged += MainPage_DataContextChanged;
}
private void MainPage_DataContextChanged( FrameworkElement sender, DataContextChangedEventArgs args )
{
vm = (MainViewModel)DataContext;
}
}
}
When I use classical binding in a UWP app (First TextBox), this code doesn't work
I see the setter being called, OnNotifyPropertyChanged gets called as well, and the handler is not null. Variable _text gets assigned its new value just fine (all uppercase), but then I never see the getter of public variable Text called.
I've also tried a converter (with ConvertBack implemented), with the same result.
Using x:Bind however (Second TextBox), it does work.
In WPF this also works as expected.
Am I missing something or has Binding changed? According to what Microsoft tells us and what I've seen it shouldn't have.
I found another Q/A in Stackoverflow which says:
The problem here is that the binding system in UWP is "intelligent". For TwoWay bindings, changes to the target will automatically propagate to the source and in this scenario, binding system assumes that the PropertyChanged event will fire for corresponding property in source and it ignores these events. So even you have RaisePropertyChanged or NotifyPropertyChanged in you source, the TextBox still won't update.
BTW I can't figure out how to create a workaround for this problem with the classic TwoWay Binding.

Toggle Button Two Way Binding Not Working (Universal Windows Platform)

I am trying to bind the "IsChecked" property on the ToggleButton to "ModelView.IsEnabled".
"ModelView.IsEnabled" is always "false"
but somehow the ToggleButton can still show as "Checked".
Is there anything wrong with the binding?
XAML
...
<Page.Resources>
<ModelView:ModelView x:Key="ModelView"/>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleButton IsChecked="{Binding Source={StaticResource ModelView}, Path=IsEnabled, Mode=TwoWay}">
<TextBlock >UWP Toggle Button</TextBlock>
</ToggleButton>
</Grid>
...
ModelView.cs
using...
namespace App2
{
class ModelView : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler CanExecuteChanged;
private bool _isEnabled;
public bool IsEnabled
{
get {
return _isEnabled;
}
set
{
_isEnabled = false;
OnPropertyChanged("IsEnabled");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
Try this, it worked to me:
1. Xaml code changes:
<Grid>
<Grid.DataContext>
<soHelpProject:MainViewModel/>
</Grid.DataContext>
<ToggleButton IsChecked="{Binding IsToggled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<TextBlock >UWP Toggle Button</TextBlock>
</ToggleButton>
</Grid>
regards,
In your class ModelView, change IsEnabled from this:
public bool IsEnabled
{
get {
return _isEnabled;
}
set
{
_isEnabled = false;
OnPropertyChanged("IsEnabled");
}
}
to this:
public bool IsEnabled
{
get {
return _isEnabled;
}
set
{
_isEnabled = value;
OnPropertyChanged("IsEnabled");
}
}
EDIT: If i use _isEnabled = !value; as you suggested, it still works, with button and state now showing opposite values:
EDIT 2: Now, if you want to properly test your binding, then you could add an extra regular button and do this:
private void button1_Click(object sender, RoutedEventArgs e)
{
myModelView.IsEnabled = !myModelView.IsEnabled;
}
so you can watch your ToggleButton switch between true and false every time you click Test Button. Please note that Test Button is not bound to anything, it's just for testing purposes. See corresponding XAML at the bottom.
The problem is that the way you're doing it, "forcing" IsEnabled to be always false, you're actually sabotaging your own code...:O)
And finally, it is not clear from your code when/where you're assigning your DataContext. Please see below how to do it.
XAML:
<Page.DataContext>
<local:MyModelView/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleButton x:Name="toggleButton1" Content="ToggleButton" IsChecked="{Binding IsEnabled, Mode=TwoWay}" HorizontalAlignment="Center"/>
<TextBlock x:Name="textBlock1" Text="{Binding IsEnabled}" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="126,0,201,286" />
<Button x:Name="button1" Click="button1_Click" Margin="127,400,0,220" Content="Test Button" Height="35" />
</Grid>
Code-behind:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
myModelView = new MyModelView();
this.DataContext = myModelView;
}
I've run into the same problem, be it not with a ToggleButton, but with a TextBox, where I wanted to format the text the user had entered.
In your case you want to change the IsChecked property in your viewmodel and have it reflected in the User Interface straight away (so always be unchecked). The reason you want that is of absolutely no importance.
The problem is that with UWP the getter of your property gets called as you would expect when you click the ToggleButton. The normal action for the ToggleButton is to change from unchecked to checked (and vice versa) and that is what happens in your case. But then you expect that NotifyPropetyChanged signals the control in the UI. And that's where it goes wrong. The getter never gets called when the setter is executed (including NotifyPropertyChanged), so the UI doesn't reflect what you did in your setter.
This is very different from what the TwoWay Binding used to do (and still does in WPF). So there is nothing wrong with your code, but it seems that the binding mechanism has changed, although Microsoft claims it didn't. If you would use x:Bind, it works fine, so hat might solve your problem.
To clarify things more I have taken your example and modified it slightly, to show the problem.
I've put a ToggleButton on the page with a TwoWay binding to a viewmodel, exactly as you did. Clicking on the ToggleButton will switch its state from checked to unchecked and vice versa, even though the setter in my viewmodel Always sets the property to false (so unchecked).
But I've also added a normal button, that I've bound to a command that also modifies the property that the ToggleButton is bound to. Clicking this button calls the setter on the property the ToggleButton is bound to. Of course the setter gets called just the same, but after that the binding to the ToggleButton gets called, so NotifyPropertyChanged in this case does cause a UI update.
If you use the debugger, you can see exactly what i mean.
So your problem can be solved by using x:Bind, or by figuring out another way to update the UI, which you shouldn't have to do if Binding was still working as it used to. Maybe Microsoft has implemented some kind of optimization that now destroys classic Binding.
No special things, just a MainPage and a viewmodel.
My code for MainPage.xaml
<Page x:Class="App10.MainPage"
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:local="using:App10"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:ViewModel x:Key="viewModel" />
</Page.Resources>
<Grid x:Name="mainGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Margin="10,20,10,0">
<Button
x:Name="Button"
Content="UWP Normal button"
Command="{Binding Source={StaticResource viewModel}, Path=SwitchIschecked}"
HorizontalAlignment="Stretch" />
<ToggleButton
x:Name="toggleButton"
Margin="0,10,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
IsChecked="{Binding Source={StaticResource viewModel}, Path=IsChecked,
Mode=TwoWay}">
<TextBlock>UWP Toggle Button</TextBlock>
</ToggleButton>
</StackPanel>
</Grid>
</Page>
The code for MainPage.xaml.cs
using Windows.UI.Xaml.Controls;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace App10
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
}
}
And the code for ViewModel.cs
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace App10
{
public class ViewModel : INotifyPropertyChanged
{
private bool _isChecked;
// property for TwoWay binding with ToggleButton
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
// extra var just to check 'value'
var _value = value;
// now always set it to false
_isChecked = false;
// Try to pass value of _isChecked to user interface
// because there is no check whether the value really
// has changed
// But this only works if the setter is not being called
// directly from the control the property is bound to
OnPropertyChanged();
}
}
private ICommand _switchChecked;
// ICommand for normal button, binding to Command
// calls method to set Property for ToggleButton
public ICommand SwitchIschecked
{
get
{
if ( _switchChecked == null )
_switchChecked = new ChangeChecked( new Action( ChangeVar ));
return _switchChecked;
}
set
{
_switchChecked = value;
}
}
// This will set the property for the ToggleButton
private void ChangeVar()
{
IsChecked = !IsChecked;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged( [CallerMemberName] string propertyName = null )
{
var handler = PropertyChanged;
handler?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
/// <summary>
/// Quick class to implement ICommand
/// </summary>
class ChangeChecked : ICommand
{
Action _execute;
public ChangeChecked( Action execute )
{
_execute = execute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute( object parameter )
{
return true;
}
public void Execute( object parameter )
{
_execute();
}
}
}
IsEnabled property is indicating whether the user can interact with the control. IsPressed is readonly property. So IsChecked is probably what you need.

binding instanciated property to UI

I have this class :
public class property : DependencyObject, INotifyPropertyChanged
{
private string _myproperty;
public string MyProperty
{
get
{
return this._myproperty;
}
set
{
this._myproperty = value;
NotifyPropertyChanged("MyProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string sproperty)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(sproperty));
}
}
}
In the main window I have created an instance of this class myclass xx = new myclass();, where I populate my property with string data and bind it to XAML like so:
<Window.Resources>
<local:property x:Key="prop"></local:property>
</Window.Resources>
In my TextBox i have set the binding :
Text="{Binding Path=MyProperty, Source={StaticResource prop}}" BorderBrush="#FFC7CACC" />
This will not work unless if i use the existing resources:
var property = (local:property)Resources["prop"];
Is there another way to update the TextBox rather than using the resources? I want to use the normal class instantiation.
if you say Text="{Binding Path=MyProperty, Source={StaticResource prop}}" BorderBrush="#FFC7CACC" />
means that your VM is an instance of property class.
Try to surround your textbox with a Grid and set the grid dataContext with an instance of your poperty clas.
I mean
<Grid DataContext="from view or from behind assign your vm= new property()">
<TextBox Text="{Binding Path=MyProperty" ....../>
</Grid>
Try this:
<Window.DataContext>
<local:property/>
<Window.DataContext>
<TextBox Text="{Binding MyProperty}"/>
After setting the data context, just try to build the application, the build will succeed if it can find the property class in the local namespace.
After building your app, if succeeded, you can try to set the binding and also the Intellisense will automatically show MyProperty in Binding Options.
If this doesn't work, try to set the data context and binding using the Properties panel. Maybe visually you can get things right.
Try it, and if it fails, tell me where it went wrong

Categories