I have this custom wpf user control:
ShowCustomer.xaml:
<UserControl x:Class="TestControlUpdate2343.Controls.ShowCustomer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBlock Text="{Binding Message}"/>
</Grid>
</UserControl>
ShowCustomer.xaml.cs:
using System.Windows.Controls;
using System;
using System.ComponentModel;
namespace TestControlUpdate2343.Controls
{
public partial class ShowCustomer : UserControl, INotifyPropertyChanged
{
#region ViewModelProperty: Message
private string _message;
public string Message
{
get
{
return _message;
}
set
{
_message = value;
OnPropertyChanged("Message");
}
}
#endregion
public ShowCustomer()
{
InitializeComponent();
DataContext = this;
Message = "showing test customer at: " + DateTime.Now.ToString();
}
#region INotifiedProperty Block
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
And I display it from this XAML:
Window1.xaml:
<Window x:Class="TestControlUpdate2343.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:TestControlUpdate2343.Controls"
Title="Window1" Height="300" Width="300">
<StackPanel HorizontalAlignment="Left" Margin="10">
<controls:ShowCustomer x:Name="ShowCustomerControl" Margin="0 0 0 10"/>
<Button Content="Refresh Control"
Click="Button_RefreshControls_Click"
Margin="0 0 0 10"/>
</StackPanel>
</Window>
And I would like to update the control (i.e. in this example show the current time) from my event handler in code behind:
using System.Windows;
namespace TestControlUpdate2343
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Button_RefreshControls_Click(object sender, RoutedEventArgs e)
{
//ShowCustomerControl.Refresh()???
}
}
}
How can I force a refresh of my custom control from code behind, or force it to reload somehow so when I click the button it shows the current time?
in Window1.xaml.cs -
private void Button_RefreshControls_Click(object sender, RoutedEventArgs e)
{
ShowCustomerControl.Refresh();
}
in ShowCustomer.xaml.cs -
public ShowCustomer()
{
InitializeComponent();
DataContext = this;
Refresh();
}
public void Refresh()
{
Message = "showing test customer at: " + DateTime.Now.ToString();
}
Hope this helps!!
Or have a LastUpdate property on ShowWindow and set that, which then regenerates the Message property.
Related
I'm trying to simplify some code by putting the ViewModel models into the code behind and binding the DataContext as "this", but it seems to work differently, in the following example:
Why is it when the button is clicked, the TextBlock bound to "Message" does not change, even though OnPropertyChanged("Message") is called?
XAML:
<Window x:Class="TestSimple223.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel HorizontalAlignment="Left">
<Button Content="Button"
Click="button1_Click" />
<TextBlock
Text="{Binding Path=Message, Mode=TwoWay}"/>
<TextBlock
x:Name="Message2"/>
</StackPanel>
</Window>
Code Behind:
using System.Windows;
using System.ComponentModel;
namespace TestSimple223
{
public partial class Window1 : Window
{
#region ViewModelProperty: Message
private string _message;
public string Message
{
get
{
return _message;
}
set
{
_message = value;
OnPropertyChanged("Message");
}
}
#endregion
public Window1()
{
InitializeComponent();
DataContext = this;
Message = "original message";
Message2.Text = "original message2";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Message = "button was clicked, message changed";
Message2.Text = "button was click, message2 changed";
}
#region INotify
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
You haven't marked your class as being available for property change notification. Change the heading to
public partial class Window1 : Window, INotifyPropertyChanged
Just because you implement the methods doesn't mean that WPF knows that a class supports change notification - you need to tell it by marking it with INotifyPropertyChanged. This way, the binding mechanism can identify your class as a potential update target.
Following this answer I was able to trigger validation error in WPF 4.0. However, it doesn't work when switching tabs. Odd.
Here's the sample. Go to tab 2, click the button, switch to tab 1, switch to tab 2 again and click the button. This second time, no error adorner is shown.
ViewModel
class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private List<string> errors;
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
errors = new List<string>();
}
private string text;
public string Text
{
get { return text; }
set
{
text = value;
RaisePropertyChanged("Text");
}
}
public void Validate()
{
errors.Clear();
if (string.IsNullOrEmpty(Text))
errors.Add("Required field");
}
public void RaisePropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
string IDataErrorInfo.Error
{
get { return errors.FirstOrDefault(); }
}
string IDataErrorInfo.this[string columnName]
{
get
{
if (columnName == "Text")
return errors.FirstOrDefault();
return null;
}
}
}
XAML
<Window x:Class="ErrorAdorner.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>
<TabControl>
<TabItem Header="First">
<TextBlock Text="Nothing here"/>
</TabItem>
<TabItem Header="Second">
<StackPanel>
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<Button Content="Trigger" Click="Button_Click" Margin="0,30,0,0"/>
</StackPanel>
</TabItem>
</TabControl>
</Grid>
Code behind
using System.Windows;
namespace ErrorAdorner
{
public partial class MainWindow : Window
{
private ViewModel model;
public MainWindow()
{
InitializeComponent();
DataContext = model = new ViewModel();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
model.Validate();
model.RaisePropertyChanged("Text");
}
}
}
Any insights?
Admittedly I am new to wpf. But i have spent some time Googling about it all and I am stumped.
in essence i want to update my TextBlock in my UI using Binding whenever my Model values change.
So this is my Model:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApplication1
{
public class MyModel : INotifyPropertyChanged
{
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
public string MyField { get; set ; }
}
}
This is my UI:
<Window x:Class="WpfApplication1.MainWindow"
xmlns:viewModels="clr-namespace:WpfApplication1"
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:WpfApplication1"
d:DataContext="{d:DesignInstance viewModels:MyModel, IsDesignTimeCreatable=True}"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<viewModels:MyModel></viewModels:MyModel>
</Window.DataContext>
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding MyModel.MyField}"></TextBlock>
<Button Content="Click Me!" Click="Button_Click" />
</StackPanel>
</Grid>
</Window>
This is my code behind:
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MyModel myModel = new MyModel();
private void Button_Click(object sender, RoutedEventArgs e)
{
myModel.MyField = "has worked";
}
}
}
When i press the button the text does not change on the UI..?
The instance you create in the code behind is not the same as you assign in xaml.
Change the button click event to
private void Button_Click(object sender, RoutedEventArgs e)
{
var model = this.DataContext as MyModel;
model.MyField = "has worked";
}
And the binding in xaml to
<TextBlock Text="{Binding MyField}"></TextBlock>
And in your viewmodel you are not calling the notify property changed. So create a private field and modify the property as below.
private string myField;
public string MyField
{
get { return this.myField; }
set { this.SetProperty(ref this.myField, value); }
}
I have a problem with data binding. A test application that I have looks as follows:
There's a mainwindow:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:WpfApplication2"
x:Name="main"
Title="MainWindow" >
<StackPanel >
<Button Content="Button" Click="Button_Click"/>
<Controls:UserControl1 />
</StackPanel>
</Window>
And a user control:
<UserControl x:Class="WpfApplication2.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="uc"
>
<Grid >
<TextBox Width="40" Text="{Binding ElementName=main,
Path=Status, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>
I want to click the button on the main window to have the value of text box in user control updated:
The code of MainWindow file:
namespace WpfApplication2
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string _status;
public string Status
{
get { return _status; }
set
{
if (value != _status)
{
_status = value;
RaisePropertyChanged("Status");
}
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (Status == "one")
Status = "two";
else
Status = "one";
}
}
}
And the code of UserControl:
namespace WpfApplication2
{
public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, e: new PropertyChangedEventArgs(propertyName));
}
public UserControl1()
{
InitializeComponent();
}
}
}
I don't understand why doesn't it work, but the PropertyChanged is always null. The example is in the simplest form I can imagine...
You are trying to access the parent window using the ElementName binding, as far as I am aware, that is not possible. You can however use a relative source binding to get the parent window:
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Status}" ... />
Follow up edit:
Your child user control should look like this:
<UserControl
...
x:Name="usr">
<Grid>
<TextBlock Text="{Binding Message, ElementName=usr}"/>
</Grid>
</UserControl>
You will then need to create a dependency property called 'Message' (This is just an example, I'm not sure what you want to call this property).
public partial class YourUserControl: UserControl
{
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
// Using a DependencyProperty as the backing store for Message. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(string), typeof(YourUserControl), new PropertyMetadata(""));
public UserControl1()
{
InitializeComponent();
}
}
Then, when you declare this in your parent user control, simply set the binding of the Message property to whatever property you need to bind to in your parent user control:
<YourNamespace:YourUserControl Message="{Binding PropertyName, ElementName=elementName}" />
I'm trying to simplify some code by putting the ViewModel models into the code behind and binding the DataContext as "this", but it seems to work differently, in the following example:
Why is it when the button is clicked, the TextBlock bound to "Message" does not change, even though OnPropertyChanged("Message") is called?
XAML:
<Window x:Class="TestSimple223.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel HorizontalAlignment="Left">
<Button Content="Button"
Click="button1_Click" />
<TextBlock
Text="{Binding Path=Message, Mode=TwoWay}"/>
<TextBlock
x:Name="Message2"/>
</StackPanel>
</Window>
Code Behind:
using System.Windows;
using System.ComponentModel;
namespace TestSimple223
{
public partial class Window1 : Window
{
#region ViewModelProperty: Message
private string _message;
public string Message
{
get
{
return _message;
}
set
{
_message = value;
OnPropertyChanged("Message");
}
}
#endregion
public Window1()
{
InitializeComponent();
DataContext = this;
Message = "original message";
Message2.Text = "original message2";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Message = "button was clicked, message changed";
Message2.Text = "button was click, message2 changed";
}
#region INotify
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
You haven't marked your class as being available for property change notification. Change the heading to
public partial class Window1 : Window, INotifyPropertyChanged
Just because you implement the methods doesn't mean that WPF knows that a class supports change notification - you need to tell it by marking it with INotifyPropertyChanged. This way, the binding mechanism can identify your class as a potential update target.