PopUp binding not done correctly - c#

I have a listview which on mouse enter to a particular column, i try to launch a popup in viewmodel class by setting isOpen to true in MyAction2() function which gets called on when user enters mouse on that column of listview.
I observe that when the mouse-enter to that column.It calls my function (MyAction2() function in ViewModel, see code written below) but even on setting the isopen variable to true in MyAction2(), The set-get method of binded isOpen not get called. Now i feel there is problem in binding. Which normally should be correct i feel some thing is missing but i dont know what.
My Xaml (containing teh opup and the column in ListView which on mouse enter calls an event called MyAction2() in ViewModel):
<Grid>
<StackPanel>
<Popup Margin="10,10,0,13" Name="Popup1" IsOpen="{Binding PopUpLaunched,Mode=TwoWay}" Placement="Top" PopupAnimation="Fade" StaysOpen="True" HorizontalAlignment="Left" VerticalAlignment="Top" Width="194" Height="200" MinWidth="500" MinHeight="500">
<StackPanel>
<Border Background="Red">
<TextBlock Name="McTextBlock" Background="LightBlue"> This is popup text </TextBlock>
</Border>
</StackPanel>
</Popup>
</StackPanel>
</Grid>
ViewModel.cs
private bool popUpLaunched;
public bool PopUpLaunched {
get {
return popUpLaunched;
} //Get set never gets called even after the popUpLaunched=true in the MyAction2() call
set {
popUpLaunched = value;
OnPropertyChanged("PopUpLaunched");
}
}
private void MyAction2(object param) //The function which gets called on mouse event but do not pop ups the popup
{
popUpLaunched = true;
}
Whats wrong and where is wrong ?

You should set the PopupLaunched property instead of setting the popUpLaunched field for the setter to get called and the PropertyChanged event to get raised:
private void MyAction2(object param)
{
PopUpLaunched = true;
}

In order to implement such a binding, you can make that property a Dependency property like this
public static readonly DependencyProperty PopUpLaunched = DependencyProperty.Register(
"popUpLaunched", typeof(bool), typeof(MainPage), new PropertyMetadata(null));
public bool popUpLaunched
{
get { return (bool)GetValue(PopUpLaunched); }
set { SetValue(PopUpLaunched, value); }
}
If you are not working on the MainPage, change that typeof(MainPage) argument respectively. And adjust getter and setter for your needs.

Related

Command does not execute on form initialize for radio button

My form has 2 radio buttons. When you press either one of them it executes a command that sets a string variable with the relative value for radio button set to true - this also has a CommandParameter that sends the value of the string Content into the Exectue function.
This works fine when a human is pressing a radio button. Variables get set and all is good.
However, I have coded in the xaml for one of the radio buttons to be set to checked by default - and this does not cause the command to be executed on startup of the form for the very first time. Hence the string variable that I hold the appropriate value for the radio button that is checked, never gets set.
How do I get my string variable recieve the value on startup from the Execute(param) method?
Here is the xaml:
<StackPanel Grid.Row="4" Grid.Column="1" Margin="3" Orientation="Horizontal">
<RadioButton GroupName="LcType" Name="TsMapPane" HorizontalAlignment="Left" Checked="TsMapPane_Checked" IsChecked="True"
Command="{Binding Path=LcTypeCommand}" CommandParameter="{Binding ElementName=TsMapPaneTextBox, Path=Text}" >
<RadioButton.Content>
<TextBlock Name="TsMapPaneTextBox" Text="TS_MAP_PANE"/>
</RadioButton.Content>
</RadioButton>
<RadioButton GroupName="LcType" Margin="10 0 0 0" Name="TsGroup" HorizontalAlignment="Left" Checked="TsGroup_Checked"
Command="{Binding Path=LcTypeCommand}" CommandParameter="{Binding ElementName=TsGroupTextBox, Path=Text}">
<RadioButton.Content>
<TextBlock Name="TsGroupTextBox" Text="TS_GROUP"/>
</RadioButton.Content>
</RadioButton>
</StackPanel>
Here is the ViewModel:
public ICommand LcTypeCommand { get; set; }
public MyViewModel()
{
LcTypeCommand = new RelayCommand((param) => LcTypeExecute(param), () => true);
}
private void LcTypeExecute(object param)
{
LcTypeName = param.ToString();
}
public string LcTypeName
{
get => _lcTypeName;
set => SetField(ref _lcTypeName, value);
}
The command is called only when the user clicks on the button.
Changing the state of the RadioButton raises the Checked and Unchecked events. You can connect a command to the Checked event, but there is no guarantee that the IsChecked property will be changed after the listener is connected. Since both are specified in XAML.
In my opinion, the most correct would be to call the command in Code Behind after XAML initialization.
InitializeComponent();
if (TsMapPane.Command is ICommand command &&
command.CanExecute(TsMapPane.CommandParameter))
{
command.Execute(TsMapPane.CommandParameter);
}
P.S. You can add the following extension method to the Solution:
public static partial class WinHelper
{
public static void TryExecute(this ICommandSource commandSource)
{
if (commandSource.Command is not ICommand command)
return;
if (command is RoutedCommand routedCommand)
{
IInputElement? target = commandSource.CommandTarget ?? commandSource as IInputElement;
if (routedCommand.CanExecute(commandSource.CommandParameter, target))
{
routedCommand.Execute(commandSource.CommandParameter, target);
}
}
else
{
if (command.CanExecute(commandSource.CommandParameter))
{
command.Execute(commandSource.CommandParameter);
}
}
}
}
Then the code will be simplified to this:
InitializeComponent();
TsMapPane.TryExecute();
shows a null value for "TsMapPane.CommandParameter"
At the time of XAML initialization, if the DataContext is assigned by an external container, bindings to the DataContext will not yet work.
Therefore, you need to execute the command once in the Loaded event:
public SomeWindow()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Loaded -= OnLoaded;
TsMapPane.TryExecute();
}

Have Button trigger UpdateSource on Control Binding without codebehind?

Say I have two fields that contain values and I want the ViewModel to only update when a save button is pressed.
From my research the main two ways of having a button trigger updates to fields is to either have codebehind calling updateSource() on the binding expressions or to add increased complexity to the ViewModel in order to handle intermediate values.
Is there really no way to trigger updateSource using only XAML?
There is no built-in way to trigger the update from XAML, but you can build your own. For example, you could use the infrastructure set up by Microsoft.Xaml.Behaviors.Wpf.
First, create an action you can execute in reply to a trigger from XAML. This action is configured by a Target/PropertyName pair. Given this information, the Action knows which Property to update in which Element (in your case, the Text property in your TextBox). These properties need to be set in XAML (see below).
The Invoke-method is invoked by the corresponding trigger declared in XAML (in your case, the Button.Click event, again, see below), you don't invoke it yourself in code.
public class UpdateBindingAction : TriggerAction<FrameworkElement>
{
public FrameworkElement Target
{
get { return (FrameworkElement)GetValue(TargetProperty); }
set { SetValue(TargetProperty, value); }
}
public static readonly DependencyProperty TargetProperty =
DependencyProperty.Register(nameof(Target), typeof(FrameworkElement), typeof(UpdateBindingAction), new PropertyMetadata(null));
public string PropertyName
{
get { return (string)GetValue(PropertyNameProperty); }
set { SetValue(PropertyNameProperty, value); }
}
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.Register("PropertyName", typeof(string), typeof(UpdateBindingAction), new PropertyMetadata(null));
protected override void Invoke(object parameter)
{
if (Target == null)
return;
if (string.IsNullOrEmpty(PropertyName))
return;
var propertyDescriptor = DependencyPropertyDescriptor.FromName(PropertyName, Target.GetType(), Target.GetType());
if (propertyDescriptor == null)
return;
Target.GetBindingExpression(propertyDescriptor.DependencyProperty).UpdateSource();
}
}
Then, create a binding in XAML that doesn't update automatically
<TextBox x:Name="txt1" Width="200" Text="{Binding String1, Mode=TwoWay, UpdateSourceTrigger=Explicit}" />
<TextBox x:Name="txt2" Width="200" Text="{Binding String2, Mode=TwoWay, UpdateSourceTrigger=Explicit}" />
Last, create a button that contains an EventTrigger for the Click-Event, which executes the UpdateSourceAction(s). The "b:" namespace is xmlns:b="http://schemas.microsoft.com/xaml/behaviors" (from Microsoft.Xaml.Behaviors.Wpf), the "local:" namespace is the one where you put the UpdateBindingAction.
<Button Margin="10" Content="Update">
<b:Interaction.Triggers>
<b:EventTrigger EventName="Click">
<local:UpdateBindingAction Target="{Binding ElementName=txt1}" PropertyName="Text" />
<local:UpdateBindingAction Target="{Binding ElementName=txt2}" PropertyName="Text" />
<!-- ... -->
</b:EventTrigger>
</b:Interaction.Triggers>
</Button>
There's some generic built-in triggers (EventTrigger, PropertyChangedTrigger, ...) and actions (ChangePropertyAction, CallMethodAction, ...), but it is very possible to implement your own additions, like this one.

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.

opening the appbar in metro style apps using binding property

My main page has the appbar and it is shared across different pages. I wrote the following code to open the appbar on the click of a gridview item.
XAML
<AppBar Opened="AppBar_Opened" IsOpen="{Binding IsAppBarOpen}">
Back end
private void Clock_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
App.ViewModel.SelectedClock = (Clock)ThemeGridView.SelectedItem;
App.WorldViewModel.IsAppBarOpen = true;
}
private void ThemeGridView_ItemClick(object sender, ItemClickEventArgs e)
{
App.ViewModel.SelectedClock = (Clock)ThemeGridView.SelectedItem;
App.WorldViewModel.IsAppBarOpen = true;
}
WorldViewModel
private bool _IsAppBarOpen;
public bool IsAppBarOpen
{
get { return _IsAppBarOpen; }
set { base.SetProperty(ref _IsAppBarOpen, value); }
}
GridView XAML
<GridView
Grid.Row="1"
Grid.Column="1"
x:Name="ThemeGridView"
ItemsSource="{Binding Clocks}"
ItemTemplate="{StaticResource WorldClockTemplate}"
SelectionChanged="Clock_SelectionChanged"
SelectionMode="None"
IsItemClickEnabled="True"
ItemClick="ThemeGridView_ItemClick"
>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
But the appbar is not popping up when i select the gridview item. There is no binding error so its really mysterious!
There is not way to bind IsOpen property according the msdn:
Note Binding to the IsOpen property doesn't have the expected results
because the PropertyChanged notification doesn't occur when the
property is set.
<AppBar Opened="AppBar_Opened" IsOpen="{Binding IsAppBarOpen, **Mode=TwoWay**}">
This works for me. I use MVVM Light Toolkit.
public bool AppBarIsOpen
{
get { return this._appBarIsOpen; }
set
{
if (this._appBarIsOpen == value) { return; }
this._appBarIsOpen = value;
this.RaisePropertyChanged("AppBarIsOpen"); // without INotifyPropertyChanged it doesn't work
}
}
<AppBar
IsSticky="True"
IsOpen="{Binding Path=AppBarIsOpen, Mode=TwoWay}">
Roman Weisert's answer correctly states the likely reason for it not working, although you also must make the binding two-way as Zack Weiner suggested (I'm not sure the reason for the latter since the binding is not working in the target-to-source direction anyway). The current value of AppBar.IsOpen may not be reflected by IsAppBarOpen of your view-model. When that's the case, and you try updating the value, it's possible that no PropertyChanged event is raised since you may not actually be updating a value. Instead, you may be just setting the value from false to false or from true to true. Most SetProperty method implementations do not raise the PropertyChanged event unless there is an actual change, and I presume yours is the same.
To fix the problem, consider modifying your view-model as follows:
public bool IsAppBarOpen
{
get { return _IsAppBarOpen; } //changes initiated from UI not reflected
set //not updated from UI
{
_IsAppBarOpen = value;
base.OnPropertyChanged();
}
}
bool _IsAppBarOpen;
The notable difference from your view-model's code, is that SetProperty is not called here so PropertyChanged is raised even when the backing store equals the newly introduced value. In case your base class differs, note that mine has an OnPropertyChanged method with the signature
void OnPropertyChanged( [CallerMemberName] string propertyName = null )
that serves to raise the PropertyChanged event.
I can see from your use of the code-behind, though, that you are not really following MVVM. If MVVM is not a concern to you, then you could forgo the IsAppBarOpen property altogether and just directly set AppBar.IsOpen. As someone who religiously adheres to MVVM, however, I do not recommend that you further head in that (sinful) direction.
I had the same issue and using Caliburn Micro for WinRT and with this code worked for me:
<AppBar IsOpen="{Binding AppBarsOpen}" Name="MainAppBar" Padding="10,0,10,0" AutomationProperties.Name="Bottom App Bar">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*" />
<ColumnDefinition Width="50*" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="LeftPanel" Orientation="Horizontal" Grid.Column="0" HorizontalAlignment="Left">
<Button Name="ShowFlyout" Style="{StaticResource BookmarksAppBarButtonStyle}" />
</StackPanel>
<StackPanel x:Name="RightPanel" Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Right">
<Button Style="{StaticResource SaveAppBarButtonStyle}" />
</StackPanel>
</Grid>
</AppBar>
And that's your property in ViewModel:
public bool AppBarsOpen
{
get { return _appBarsOpen; }
set
{
if (value.Equals(_appBarsOpen)) return;
_appBarsOpen = value;
NotifyOfPropertyChange(() => AppBarsOpen);
}
}
Had the same issue, solved it by adding the Closed event and updating the ViewModel from the code behind. Saw no other way since TwoWay binding was not working as Roman pointed out.
XAML
<AppBar x:Name="BottomAppBar1"
AutomationProperties.Name="Bottom App Bar"
Closed="BottomAppBar1_Closed"
IsOpen="{Binding IsOpen, Mode=TwoWay}"
IsSticky="True">
C# Code behind
private void BottomAppBar1_Closed(object sender, object e)
{
MainViewModel vm = this.DataContext as MainViewModel;
vm.IsOpen = false;
}
C# MainViewModel
public const string IsOpenPropertyName = "IsOpen";
private bool isOpen = false;
/// <summary>
/// Sets and gets the IsOpen property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public bool IsOpen
{
get
{
return isOpen;
}
set
{
RaisePropertyChanging(IsOpenPropertyName);
isOpen = value;
RaisePropertyChanged(IsOpenPropertyName);
}
}
You should bind both IsOpen and IsSticky two way because otherwise you will get problems with for example having to tap two time to unselect an item (once to close the app bar and once for unselecting) and also it's the will help having your app bar behave more standarly (will prevent the app bar to pop down on tap when an item is selected).
To show the app bar you will need to do the following (the order is important):
this.IsAppBarSticky = true;
this.IsAppBarOpen = true;
and to hide it, do the following:
this.IsAppBarSticky = false;
this.IsAppBarOpen = false;
Another way to make this work without having to use a codebehind handler for app bar closed event:
public class AppBarClosedCommand
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand),
typeof(AppBarClosedCommand), new PropertyMetadata(null, CommandPropertyChanged));
public static void SetCommand(DependencyObject attached, ICommand value)
{
attached.SetValue(CommandProperty, value);
}
public static ICommand GetCommand(DependencyObject attached)
{
return (ICommand)attached.GetValue(CommandProperty);
}
private static void CommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Attach click handler
(d as AppBar).Closed += AppBar_onClose;
}
private static void AppBar_onClose(object sender, object e)
{
// Get GridView
var appBar = (sender as AppBar);
// Get command
ICommand command = GetCommand(appBar);
// Execute command
command.Execute(e);
}
}
then in the XAML you can use it like :
common:AppBarClosedCommand.Command="{Binding AppBarClosedCommand}"
with the command function looking like:
public void OnAppBarClosed()
{
AppBarOpen = false;
}

WPF Binding to variable / DependencyProperty

I'm playing around with WPF Binding and variables. Apparently one can only bind DependencyProperties. I have come up with the following, which works perfectly fine:
The code-behind file:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string Test
{
get { return (string)this.GetValue(TestProperty); }
set { this.SetValue(TestProperty, value); }
//set { this.SetValue(TestProperty, "BBB"); }
}
public static readonly DependencyProperty TestProperty = DependencyProperty.Register(
"Test", typeof(string), typeof(MainWindow), new PropertyMetadata("CCC"));
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Test);
Test = "AAA";
MessageBox.Show(Test);
}
}
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBox Height="31" HorizontalAlignment="Left" Margin="84,86,0,0" Name="textBox1" VerticalAlignment="Top" Width="152"
Text="{Binding Test, Mode=TwoWay, diag:PresentationTraceSources.TraceLevel=High}"/>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="320,85,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<TextBox Height="31" HorizontalAlignment="Left" Margin="84,138,0,0" Name="textBox2" Text="{Binding Test, Mode=TwoWay}" VerticalAlignment="Top" Width="152" />
</Grid>
The two TextBoxes update one an other. And the Button sets them to "AAA".
But now I replaced the Setter function with the one that is commented out (simulating some manipulation of the given value). I would expect that whenever the property value is changed it will be reset to "BBB". It does so when you press the button, that is when you set the property in code. But it does for some reason not affect the WPF Bindings, that is you can change the TextBox contents and thus the property, but apparently the Setter is never called.
I wonder why that is so, and how one would go about to achive the expected behaviour.
The CLR Property wrapper for a Dependency Property is never guaranteed to be called and therefore, you should never place any additional logic there. Whenever you need additional logic when a DP is changed, you should use the property changed callback.
In your case..
public string Test
{
get { return (string)this.GetValue(TestProperty); }
set { this.SetValue(TestProperty, value); }
}
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test",
typeof(string),
typeof(MainWindow),
new PropertyMetadata("CCC", TestPropertyChanged));
private static void TestPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
MainWindow mainWindow = source as MainWindow;
string newValue = e.NewValue as string;
// Do additional logic
}
Your change will not affect the binding because the XAML will call SetValue directly, instead of calling your property setter.That is how the dependency property system works.When a dependency property is registered a default value can be specified.This value is returned from GetValue and is the default value for your property.
Check the link below and read through to Robert Rossney's post to get a fair overview
WPF: What distinguishes a Dependency Property from a regular CLR Property?
also don't miss
http://msdn.microsoft.com/en-us/library/ms753358.aspx
and
http://msdn.microsoft.com/en-us/library/ms752914.aspx
Also note that unlike in normal CLR properties any custom logic you write in the setter will not be executed in Dependency Properties,instead you have to use the PropertyChangedCallback mechanism
http://blogs.msdn.com/b/delay/archive/2010/03/23/do-one-thing-and-do-it-well-tip-the-clr-wrapper-for-a-dependencyproperty-should-do-its-job-and-nothing-more.aspx

Categories