StackPanel visibility not updated when dependency property changed - c#

I'm currently developping an universal app in C#/XAML with MVVM (not MVVM Light) and I have trouble for the XAML part.
I'd like to display one or another StackPanel when a dependency property changed in my ViewModel. I think the code speaks for itself.
<StackPanel Visibility="{Binding MyProperty, Converter={StaticResource BooleanToVisibilityConverter}}">
<!-- Some content -->
</StackPanel>
<StackPanel Visibility="{Binding MyProperty, Converter={StaticResource InvertBooleanToVisibilityConverter}}">
<!-- Some another content -->
</StackPanel>
And here is the definition of the dependency property.
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register(
"MyProperty",
typeof (bool),
typeof (MyViewModel),
new PropertyMetadata(true));
public bool MyProperty
{
get { return (bool) GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); OnPropertyChanged(); // Implemented by ReSharper }
}
I guess you figure it out that MyProperty is a boolean that I convert into a Visibility via the converters. So, when MyProperty changed in the ViewModel, the view isn't updated.
I already tried to use the UpdateSourceTrigger property but it's not working. Also, I have no binding error and converters are working fine (I only see one StackPanel at the app launch).
Please keep in mind that I don't want to use the code behind part unless there is no other solution.
Thanks for your help.

I finaly gave up and used the code behind part and it's working fine now.

Are your <StackPanel>s part of some UserControl? If not, why are you using DependencyProperty?
Your implementation is quite off as well.
Lets assume for a minute that this is not part of a Custom Control (correct me -- if I'm wrong, I will rewrite the solution)
So you have a ViewModel and you want to hook up some Properties to it. You really don't need to implement DependencyProperty to do what you want to do, but I will entertain you by implementing it your way.
This is a sample ViewModel with 1 (one) property
using Windows.UI.Xaml;
using System.ComponentModel;
// very simple view model
class MyViewModel : DependencyObject, INotifyPropertyChanged
{
// implement INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// register
public static DependencyProperty FooterTitleProperty = DependencyProperty.Register("FooterTitle", typeof(string), typeof(MyViewModel),
new PropertyMetadata(string.Empty, OnFooterTitlePropertyChanged));
// the actual property
public string FooterTitle
{
get { return (string) GetValue(FooterTitleProperty); }
set
{
SetValue(FooterTitleProperty, value);
}
}
// this will fire when the property gets change
// it will call the OnPropertyChanged to notify the UI element to update its layout
private static void OnFooterTitlePropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
MyViewModel mvm = dependencyObject as MyViewModel;
mvm.OnPropertyChanged("FooterTitle");
}
}
To test out the code we will make a very simple XAML form
<Grid x:Name="ContentPanel">
<StackPanel>
<TextBlock x:Name="tb" Text="{Binding FooterTitle}" FontSize="48"></TextBlock>
<Button Content="Test Property" Click="Button_Click_1"></Button>
</StackPanel>
</Grid>
When you click on the button we will change the Textbox's Text
public sealed partial class MainPage : Page
{
// create the view model
MyViewModel vm = new MyViewModel();
public MainPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
// set the text we initial want to display
vm.FooterTitle = "default text";
// set the DataContext of the textbox to the ViewModel
tb.DataContext = vm;
}
// after the button is click we change the TextBox's Text
private void Button_Click_1(object sender, RoutedEventArgs e)
{
// change the text
vm.FooterTitle = "Test Property Has Changed.";
// what happens is the Setter of the Property is called first
// after that happens it launches the `OnFooterTitlePropertyChanged` event
// that we hook up with the Register function.
// `OnFooterTitlePropertyChanged` launches the INotifyPropertyChanged event
// then finally the TextBox will updates it's layout
}
}
At this point you can guess you really don't need the DependencyProperty and say why can't I just launch the INotifyPropertyChanged in the Setter instead? Well you can and it is probably the prefer method.
If all these is part of a UserControl then I can see using a DependencyProperty then in the OnFooterTitlePropertyChanged event you can set the
name_of_textbox.Text = FooterTitle;

I think property name should be given with OnPropertyChanged method, like this;
public bool MyProperty
{
get { return (bool) GetValue(MyPropertyProperty); }
set {
SetValue(MyPropertyProperty, value);
OnPropertyChanged("MyProperty");
}
}
https://learn.microsoft.com/en-us/uwp/api/windows.ui.xaml.data.inotifypropertychanged.propertychanged

Related

where to put the textbox that for different UserControls to use?

In my WPF program. I have a DataModel.cs, MainWindow.xaml & MainWindow.xaml.cs and many UserControl.xaml & UserControl.xaml.cs which are integrated into MainWindow.xaml in this way:
<Border...>
<UserControl1/>
</Border>
<Border...>
<UserControl2/>
</Border>
...
I am not sure if it can be called MVVM?
Some of my UserControl.xaml.cs need to use the same string for connection which should be typed on the UI textbox somewhere like this:
DataModel.connection.Connect("[textbox.text]");
My question is where do I put this textbox so that each UserControl can get access to it and how? Thanks.
You create a string property in your DataModel class:
public class DataModel : INotifyPropertyChanged
{
private string _text;
public string Name
{
get { return _text; }
set
{
_text = value;
OnPropertyChanged(nameof(Name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
...and bind the TextBox in the view to this string property:
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
Provided that you set the DataContext of the window to an instance of your DataModel class:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new DataModel();
}
}
...the source property (Text) will be updated whenever you type something into the TextBox. If you let the UserControls inherit the DataContext from the parent window, i.e. don't set the DataContext of the UserControls explicitly somewhere, they can bind to the very same Text property and get the latest value "automatically" as the DataModel implement the INotifyPropertyChanged interface and raise the PropertyChanged event.
This is roughly how basic MVVM works.
I think you need read more on MVVM, short answer, as long as you are not referencing and UI elements directly in you ViewModel, you can consider it MVVM.
There are multiple ways you can do it, I would define and interface with
public interface IProvideTextProperty {string SomeText{get;set;}}
Implement this interface in all UserControls that use this property
ON the UI have a textbox in MainWindow, Bind the textproperty of this TextBox to SomeText(viewmodel of MainWindow also implements IProvideTextProperty), in the setter, loop though all the viewmodels that are IProvideTextProperty and set the SomeProperty.

Synchronizing Dependency Properties (View) with Properties (ViewModel)

Does anybody know how I can synchronize my properties, which are in a ViewModel, with my Dependency Properties, which are in the View?
I am trying to make a UserControl, which will then be hosted by a WPF-Window (MainWindow.xaml). The UserControl has an own ViewModel which contains ICommands and properties.
The problem is, that I also have to return certain properties to the MainWindow(.xaml) and also set them.
Currently my classes are looking like that:
MainWindow.xaml
<TextBox Name="tbInput" VerticalAlignment="Top" HorizontalAlignment="Stretch" Grid.Row="0"></TextBox>
<local:View x:Name="appEntryView" Pfad="{Binding ElementName=tbInput, Path=Text}" Grid.Row="1" Margin="10"/>
View.xaml
<UserControl x:Class="DependencyProperties.Test"
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:DependencyProperties_Intro"
x:Name="obj"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding ElementName=obj, Path=Pfad}"/>
</Grid>
View.xaml.cs
public partial class View: UserControl, INotifyPropertyChanged
{
public String Pfad
{
get { return (String)GetValue(PfadProperty); }
set { SetValue(PfadProperty, value); OnNotifyPropertyChanged("Pfad"); }
}
// Using a DependencyProperty as the backing store for Path. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PfadProperty =
DependencyProperty.Register("Pfad", typeof(String), typeof(GraphSharpTest), new PropertyMetadata(default(string)));
public View()
{
InitializeComponent();
this.DataContext = new ViewModel();
var name = "Pfad";
var binding = new Binding(name) { Mode = BindingMode.TwoWay };
this.SetBinding(PfadProperty, binding);
}
}
ViewModel.cs
public class ViewModel: INotifyPropertyChanged
{
private String m_Pfad;
public String Pfad
{
get { return m_Pfad; }
set { m_Pfad = value; OnNotifyPropertyChanged("Pfad"); }
}
public void OnNotifyPropertyChanged(String info)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(info));
}
public event PropertyChangedEventHandler PropertyChanged;
}
The dependency property works fine, but the setter method of "Pfad" in the ViewModel never gets called at all.
Thanks in advance!
Raising PropertyChanged in the CLR properties of dependency properties is a common mistake. You should not place any code there as it is not used by bindings at all. They exist merely for setting the property once in code or XAML, thus you also will not hit any breakpoints you set there.
var name = "Pfad";
var binding = new Binding(name) { Mode = BindingMode.TwoWay };
this.SetBinding(PfadProperty, binding);
I take it you want to forward the value to your view-model. This is not going to work as you can only bind the property once. Right now you also bind the property here:
<local:View x:Name="appEntryView" Pfad="{Binding ElementName=tbInput, Path=Text}" Grid.Row="1" Margin="10"/>
You could subscribe to the dependency property changes using the respective meta data when registering it, providing a callback, there you could set the value in the view-model.
The thing is: The view-model is private to the View, there really is no point in doing this synchronization if no-one has access to the data. You probably want the property to be either settable from the outside, treating the UserControl more like a control, discarding the view-model, or you want the view-model to be passed from outside as DataContext, and the view binds directly to it.
You need to be careful with explicitly setting the DataContext of UserControls in their definition, as it can obfuscate what is happening and lead to bindings unexpectedly breaking. If you want to set properties on the UserControl instance i would recommend avoiding it.

Create a proxy for a dependency property

I am trying to create a simple dependency property proxy. I made a custom control, it's a file picker, which is made off a textbox (name: "TextBox_FilePath") and a button showing the open file dialog.
As I am making a reusable control I'd like it to have a "SelectedFilePath" property. As the Text property seems to be perfect for my control to be the "SelectedFilePath" property, I'd just like to proxy these dependency property.
The first approach I made was:
public static readonly DependencyProperty SelectedFilePathProperty = TextBox.TextProperty;
public string SelectedFilePath
{
get { return (string) this.TextBox_FilePath.GetValue(SelectedFilePathProperty); }
set { this.TextBox_FilePath.SetValue(SelectedFilePathProperty, value); }
}
which worked, but throwed an exception when trying to bind to that property. Then I came off with:
public static readonly DependencyProperty SelectedFilePathProperty =
DependencyProperty.Register("SelectedFilePath", typeof (string), typeof (FilePicker), new PropertyMetadata(default(string)));
public string SelectedFilePath
{
get { return (string) this.TextBox_FilePath.GetValue(SelectedFilePathProperty); }
set { this.TextBox_FilePath.SetValue(SelectedFilePathProperty, value); }
}
which does work, but I've got no idea why?! Where did I specify I wanted the text property of the textbox?
What am I missing to simply proxy out that dependency property?
EDIT:
The solution with AddOwner doesn't work too, it throws an Excetion saying "binding can only be applied on a dependency property". Code:
public static readonly DependencyProperty SelectedFilePathProperty =
TextBox.TextProperty.AddOwner(typeof(FilePicker));
public string SelectedFilePath
{
get { return (string)this.TextBox_FilePath.GetValue(SelectedFilePathProperty); }
set { this.TextBox_FilePath.SetValue(SelectedFilePathProperty, value); }
}
What don't I understand?
EDIT2:
For everybody else having issues understanding the answer, I've made a little graphic
The first approach does not work because the property is registered only for the TextBox, adding a reference in another class does nothing.
The second one just creates a whole new string property.
If you really want to reuse the TextBox.TextProperty call AddOwner on it.
e.g.
public static readonly DependencyProperty SelectedFilePathProperty =
TextBox.TextProperty.AddOwner(typeof(FilePicker));
(Note that this property is registered as "Text", so you probably should just create a new property with the name you want as you did already. I would also recommend to set metadata flags to bind two-way by default if you want to have the same binding behaviour as TextBox.Text.)
This solution is a little tricky but works.
Given this user control:
<Grid>
<StackPanel>
<WpfApplication1:FilePicker SelectedFilePath ="{Binding MyProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="{Binding MyProperty}" />
</StackPanel>
</Grid>
And its viewmodel:
public class MainWindowViewModel : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string e)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(e));
}
#endregion
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
OnPropertyChanged("MyProperty");
}
}
}
XAML for FilePicker control:
<Grid>
<TextBox x:Name="TextBox_FilePath" DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type WpfApplication1:FilePicker}}}" Text="{Binding SelectedFilePath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
CodeBehind for FilePicker control:
public partial class FilePicker : UserControl
{
public FilePicker()
{
InitializeComponent();
}
/* private PROXY DP*/
private static readonly DependencyProperty TextProperty =
TextBox.TextProperty.AddOwner(typeof(FilePicker));
/* public DP that will fire getter/setter for private DP */
public static readonly DependencyProperty SelectedFilePathProperty =
DependencyProperty.Register("SelectedFilePath", typeof(string), typeof(FilePicker), new PropertyMetadata(default(string)));
public string SelectedFilePath
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
Works like a charm.
As I had issues understanding H.B.s answer I made a little graphic which helped me to understand what's going on under the hood. Here it is;
Maybe it helps someone else :)

WPF UserControl Binding Problem

I like to create a UserControl with own Header Property.
public partial class SomeClass: UserControl, INotifyPropertyChanged
{
public SomeClass()
{
InitializeComponent();
}
private string header;
public string Header
{
get { return header; }
set
{
header = value;
OnPropertyChanged("Header");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
in UserContol xaml:
Label Name="lbHeader" Grid.Column="0" Content="{Binding Path=Header}"
If I set the value: AA2P.Header = "SomeHeeaderText"; than the label.Caption will not changed. How can I solve that problem?
In Windows xaml:
uc:SomeClass x:Name="AA2P"
If I give directly a value to label (lbHeader.Content = header;) instead of OnPropertyChanged("Header"); its work but, why it does not work with OnPropertyChanged?
I need to use DataContext for somethig else. I try to use dependency property but something is wrong.
public partial class tester : UserControl
{
public tester()
{
InitializeComponent();
}
public string Header
{
get { return (string)GetValue(MyDependencyProperty); }
set { SetValue(MyDependencyProperty, value); }
}
public static readonly DependencyProperty MyDependencyProperty =
DependencyProperty.Register("MyDependencyProperty", typeof(string), typeof(string));
}
<UserControl ... x:Name="mainControl">
<TextBlock Text="{Binding ElementName=mainControl, Path=MyDependencyProperty}"/>
</UserControl>
<Window ...>
<my:tester Header="SomeText" />
</Window>
It does not work. What I do wrong?
Thanks!
The easiest approach is to just the DataContext of your object. One way of doing that is directly in the constructor like this:
public SomeClass()
{
InitializeComponent();
DataContext = this;
}
Setting the DataContext will specify where new data should be fetched from. There are some great tips and information in the article called WPF Basic Data Binding FAQ. Read it to better understand what the DataContex can be used for. It is an essential component in WPF/C#.
Update due to update of the question.
To my understanding you should change the first argument of DependencyProperty.Register to the name of the property that you want to bind to, here "Header" as well as the second argument to the type of your class, here SomeClass. That would leave you with:
public static readonly DependencyProperty MyDependencyProperty =
DependencyProperty.Register("Header", typeof(SomeClass), typeof(string));
But i seldom use dependency properties so I am not positive that this is it, but its worth a try..
If you need the Data context for something else. You can also utilize the ElementName property in the Binding.
<UserControl
x:Class="MyControl.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="mainControl">
<TextBlock Text="Binding ElementName=mainControl, Path=MyDependencyProperty}"/>
</UserControl>
[Edit]
I should add something. Make the "Header" property a dependency property, this will make your live much easier. In UI Controls you should make property almost always a dependency property, every designer or user of your control will thank you.
The UserControl itself needs the DataContext of where it is used later. But the controls inside the UserControl need the UserControl as their DataContext, otherwise they also will inherit the DataContext from the later usage context. The trick is to set the DataContext of the UserControl's child to that of the UserControl, so it now can use the dependency properties of the UserControl.
<UserControl x:Class="MyControl.MyUserControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType=UserControl,AncestorLevel=1}}">...</Grid>
</UserControl>
If you do this this way the children of the Grid can have simple {Binding dp's name} without additionally ElementName parameters.

WPF Data Binding Architecture Question

I'm trying to learn how to use WPF binding and the MVVM architecture. I'm running into some trouble with Dependency Properties. I've tried to control the visibility of an item on the view by binding it to a DependencyProperty in the DataContext, but it doesn't work. No matter what I set the GridVisible value to in the constructor of the view model below, it is always displayed as visible when I run the code.
Can anyone see where I'm going wrong?
C# code (ViewModel):
public class MyViewModel : DependencyObject
{
public MyViewModel ()
{
GridVisible = false;
}
public static readonly DependencyProperty GridVisibleProperty =
DependencyProperty.Register(
"GridVisible",
typeof(bool),
typeof(MyViewModel),
new PropertyMetadata(false,
new PropertyChangedCallback(GridVisibleChangedCallback)));
public bool GridVisible
{
get { return (bool)GetValue(GridVisibleProperty); }
set { SetValue(GridVisibleProperty, value); }
}
protected static void GridVisibleChangedCallback(
DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
// Do other stuff in response to the data change.
}
}
XAML code (View):
<UserControl ... >
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</UserControl.Resources>
<UserControl.DataContext>
<local:MyViewModel x:Name="myViewModel" />
</UserControl.DataContext>
<Grid x:Name="_myGrid"
Visibility="{Binding Path=GridVisible,
ElementName=myViewModel,
Converter={StaticResource BoolToVisConverter}}">
<!-- Other elements in here -->
</Grid>
</UserControl>
I've looked at several tutorials online, and it seems like I'm correctly following what I've found there. Any ideas? Thanks!
Take the ElementName off your binding, that doesn't seem correct.
Change it to:
<Grid x:Name="_myGrid"
Visibility="{Binding Path=GridVisible,
Converter={StaticResource BoolToVisConverter}}">
Have your ViewModel implement INotifyPropertyChanged rather than inheriting from DependencyObject.
Implement the interface and raise PropertyChanged from your setter for the property.
private bool gridVisible;
public bool GridVisible
{
get { return gridVisible; }
set
{
gridVisible = value;
OnPropertyChanged("GridVisible");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The point of setting the ViewModel as DataContext is to enable easy relative bindings, all bindings where you only specify the Path take the DataContext as source which is inherited throughout the UserControl (unless it is set otherwise, for example in the templated items of an ItemsControl)
So once the DataContext is set on the UserControl you normally do not specify any source when binding to the VM. (Sources are ElementName, RelativeSource and Source)
Further i personally would not make ViewModels inherit from DependencyObject since this introduces thread-affinity, also the point of DependencyProperties is making sparse data structures more efficient by not creating unnecessary fields in all of them (ViewModels normally are quite the opposite of sparse).

Categories

Resources