2 WPF and a DLL, model change notification - c#

I'm trying to make two WPF projects and a DLL project in my solution following MVVM.
A first WPF to be used as a management panel for the second WPF (ex: you write text, press a button and the text is displayed in the second WPF Window).
I want to put my Models in the DLL.
My problem is that i don't know how to display the text (notify ?) in the second WPF.
I implemented INotifyPropertyChanged in the viewmodel.
And then I'm stuck, I don't know what to do...
My Solution looks like this :
WPF_Solution
DisplayWPF
MainWindow.xaml
Dll_mvvm
Text.cs :
ManagementWPF
DisplayViewModel.cs
MainWindow.xaml
Both Display and Management refer to the DLL.
Text.cs :
public class Text
{
string _textToDisplay;
/// <summary>
/// Text to display on screen
/// </summary>
public string TextToDisplay
{
get { return _textToDisplay; }
set { _textToDisplay = value; }
}
}
DisplayViewModel.cs :
public class DisplayViewModel : INotifyPropertyChanged
{
private Text _text;
/// <summary>
/// Constructs the default instance of a ToDisplayViewModel
/// </summary>
public DisplayViewModel()
{
_text = new Text();
}
/// <summary>
/// Accessors
/// </summary>
public Text Text
{
get { return _text; }
set { _text = value; }
}
public string TextToDisplay
{
get { return Text.TextToDisplay; }
set
{
if (Text.TextToDisplay != value)
{
Text.TextToDisplay = value;
RaisePropertyChanged("TextToDisplay");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
// take a copy to prevent thread issues
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Management Panel :
public partial class MainWindow : Window
{
private DisplayViewModel _displayThings;
private Affichage.MainWindow _displayer = new Affichage.MainWindow();
public MainWindow()
{
InitializeComponent();
_displayThings = (DisplayViewModel)base.DataContext;
_displayer.Show();
}
private void disp_btn_Click(object sender, RoutedEventArgs e)
{
_displayThings.TextToDisplay = textBox.Text;
}
}
And the WPF are just a button and a textbox in the Control window, and a textbox in the Display window. Linked to the viewmodel
<Window.DataContext>
<!-- Declaratively create an instance of DisplayViewModel -->
<local:DisplayViewModel />
</Window.DataContext>
The TextBox is binded with Text="{Binding TextToDisplay}"
Should my Viewmodel be share din the DLL too ?
How to notify the other project that ther was a change in the model ?

Remove the following markup as it will create a new instance of the DisplayViewModel class:
<Window.DataContext>
<!-- Declaratively create an instance of DisplayViewModel -->
<local:DisplayViewModel />
</Window.DataContext>
...and assign the DataContext property of the child window to the instance of the DisplayViewModel class that you are actually setting the TextToDisplay property of:
public partial class MainWindow : Window
{
private DisplayViewModel _displayThings = = new DisplayViewModel();
private Affichage.MainWindow _displayer = new Affichage.MainWindow();
public MainWindow()
{
InitializeComponent();
_displayer.DataContext = _displayThings;
_displayer.Show();
}
private void disp_btn_Click(object sender, RoutedEventArgs e)
{
_displayThings.TextToDisplay = textBox.Text;
}
}

Related

How do I bind FontSize for WPF TextBox in XAML to a class member variable?

How do I bind FontSize for WPF TextBox in XAML to a class member variable?
I have a collection of fonts that I use through the application.
I would like to change the values of those fonts dynamically in my code behind and then have the changes reflected during runtime.
How do I achieve this?
Here is what my class definition looks like
public ClassFoo
{
public double FontSize {get; set;}
}
This is how I define my class in MainWindow.xaml.cs:
public ClassFoo SampleClass;
Here is my what my XAML looks like:
<TextBlock Name="txtSample" Text="SomeText"
FontSize="{Binding SampleClass.FontSize}"/>
Then at runtime, I instantiate the class and initialize it:
SampleClass = new ClassFoo()
{
FontSize = 16;
}
I would create it like that:
public class MainWindow : Page
{
public Foo Foo { get; set; }
public MainWindow()
{
DataContext = this;
}
}
public class Foo : INotifyPropertyChanged
{
private double _fontSize;
public double FontSize
{
get { return _fontSize; }
set
{
_fontSize = value;
OnPropertyChanged(nameof(FontSize));
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and then call it like:
<TextBlock Name="txtSample" Text="SomeText"
FontSize="{Binding Foo.FontSize}"/>
Most likely you need a DataContext = this; in your constructor for Mainwindow.xaml.cs. You also need in Mainwindow.xaml.cs that returns SampleClass.
You can only bind to public properties so the first thing to do would be to make SampleClass a property:
public ClassFoo SampleClass { get; set; }
And if you intend to set it dynamically at runtime after the constructor of the window has returned, the window should implement the INotifyPropertyChanged interface and raise change notfications for the taget property to get automatically updated.
Finally the source of the binding must be set to the window somehow. You could set the Source property of the binding explicitly or set the DataContext of the TextBlock or any of its parent element to an instance of the window.
Try this implementation of the MainWindow class together with the XAML markup you posted:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
this.Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
SampleClass = new ClassFoo()
{
FontSize = 16
};
}
private ClassFoo _sampleClass;
public ClassFoo SampleClass
{
get { return _sampleClass; }
set { _sampleClass = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Disable ComboBox of a UserControl from another UserControl without MVVM

I have a UserControl Person_UC and Student_UC. There is a ComboBox in Student_UC which I want to disable it from Person_UC.
But its not working. I want to accomplish this without MVVM.
public partial class Person_UC : UserControl
{
public Person_UC()
{
InitializeComponent();
Student_UC su = new Student_UC();
su.myComboBoxName.IsEnabled = false;
}
}
Without MVVM it would be quite hard to solve. You have to manipulate the same instance of Student_UC which is currently used.
Actually, you're instantiating a new Student_UC and disabling its ComboBox, but you're not doing anything with your variable "su". Did you assign it somewhere?
Basically, you should have one ViewModel per UserControl, so a ViewModel for Person_UC and a ViewModel for Student_UC.
Warning, this solution requires you to use a MVVM Framework like MVVM Light (https://mvvmlight.codeplex.com) for sending messages.
One standard way would be sending a message. Bind your Loaded event of your Person_UC to a method in your code-behind like so:
<UserControl x:Class="YourAssembly.Person_UC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Person_UC"
Loaded="Loaded">
<Grid Name="RootGrid">
</Grid>
</UserControl>
And:
public partial class Person_UC : UserControl
{
public Person_UC()
{
InitializeComponent();
}
private void Loaded(object sender, RoutedEventArgs e)
{
//Use a Message to notify your Student_UC's ViewModel that you would like to disable its ComboBox
Messenger.Default.Send<ChangeComboBoxEnabilityMessage>(new ChangeComboBoxEnabilityMessage(false));
}
}
Then, when you receive the message within the Student_UC's ViewModel, you have to pass this information to the view. Basically, you can bind IsEnable property of the ComboBox to a property in its ViewModel, that you will set to false when the message is received.
public class Student_UC_ViewModel : INotifyPropertyChanged
{
public Student_UC_ViewModel()
{
//Register your message
Messenger.Default.Register<ChangeComboBoxEnabilityMessage>(message => ComboBoxIsEnabled = message.ComboBoxIsEnabled);
}
private bool _comboBoxIsEnabled;
public bool ComboBoxIsEnabled
{
get
{
return _comboBoxIsEnabled;
}
set
{
_comboBoxIsEnabled = value;
OnPropertyChanged("ComboBoxIsEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
And your message class:
public class ChangeComboBoxEnabilityMessage : MessageBase
{
public ChangeComboBoxEnabilityMessage(bool comboBoxEnabled)
{
ComboBoxIsEnabled = comboBoxEnabled;
}
public bool ComboBoxIsEnabled
{
get;
set;
}
}
I let you bind your IsEnable property of your ComboBox in your Student_UC xaml to the property of its ViewModel (i.e ComboBoxIsEnabled).
Don't forget to make sure your DataContext is set:
public partial class Student_UC : UserControl
{
public Person_UC()
{
InitializeComponent();
DataContext = new Student_UC_ViewModel();
}
...
}
Also, take care of your binding issues, see your output console in Visual Studio.

How to bind textbox object to ViewModel

Trying to make my first application with the simple logging function to the TextBox on main form.
To implement logging, I need to get the TextBox object into the logger's class.
Prob - can't do that :) currently have no error, but as I understand the text value of TextBox is binding to my ViewModel, because getting 'null reference' exception trying to execute.
Logger.cs
public class Logger : TextWriter
{
TextBox textBox = ViewModel.LogBox;
public override void Write(char value)
{
base.Write(value);
textBox.Dispatcher.BeginInvoke(new Action(() =>
{
textBox.AppendText(value.ToString());
}));
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}
ViewModel.cs
public class ViewModel
{
public int ThreadCount { get; set; }
public int ProxyTimeout { get; set; }
public static TextBox LogBox { get; set; }
//private TextBox _LogBox;
//public TextBox LogBox {
// get { return _LogBox; }
// set {
// _LogBox = value;
// }
//}
}
launching on btn click, MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Logger logger = new Logger();
logger.Write("ewgewgweg");
}
}
MainWindow.xaml
<Window
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:tools"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" x:Class="tools.MainWindow"
mc:Ignorable="d"
Title="Tools" Height="399.387" Width="575.46">
<TextBox x:Name="logBox"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto" HorizontalAlignment="Left" Height="137" Margin="10,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="394" Text="{Binding Path = LogBox, Mode=TwoWay}"/>
You have several issues in your code:
Don't bring controls (TextBox) in your viewmodel, if you do there's no use in trying to do MVVM.
The Text property in XAML has to be of the type String or something that can be converted to a string. You're binding a control, which will result in showing System.Windows.Controls.TextBox (result of .ToString()) on your screen instead of actual text.
Your LogBox property should implement INotifyPropertyChanged
You don't want TwoWay binding, as the text flows from your logger to the UI, you don't need it to flow back. You might even consider using a TextBlock instead or make the control readonly so people can't change the content.
You don't want static properties or static viewmodels, read up on dependency injection on how to pass dependencies.
You will be flooding your UI thread by appending your characters one by one. Consider using another implementation (but I won't go deeper into this for this answer).
Keeping all above in mind, I transformed your code to this.
MainWindow.xaml
<TextBox x:Name="logBox"
HorizontalAlignment="Left" VerticalAlignment="Top" Height="137" Margin="10,222,0,0"
TextWrapping="Wrap" Width="394" Text="{Binding Path = LogBox}"/>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private Logger _logger;
public MainWindow()
{
InitializeComponent();
var viewModel = new ViewModel();
DataContext = viewModel;
_logger = new Logger(viewModel); // passing ViewModel through Dependency Injection
}
private void button1_Click(object sender, RoutedEventArgs e)
{
_logger.Write("ewgewgweg");
}
}
ViewModel.cs
public class ViewModel : INotifyPropertyChanged
{
public int ThreadCount { get; set; }
public int ProxyTimeout { get; set; }
private string _logBox;
public string LogBox
{
get { return _logBox; }
set
{
_logBox = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Logger.cs
public class Logger : TextWriter
{
private readonly ViewModel _viewModel;
public Logger(ViewModel viewModel)
{
_viewModel = viewModel;
}
public override void Write(char value)
{
base.Write(value);
_viewModel.LogBox += value;
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}
You can use string instead of TextBox as follow as
In view model class
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _logBox;
public string LogBox
{
get {return _logBox;}
set
{
if(value != _logBox)
{
_logBox=value;
OnPropertyChanged("LogBox");
}
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
and in writer method you just
public void writer (string str)
{
ViewModel.LogBox = str;
}
You can define ViewModel as static or create new object from ViewModel and access the object in logger class as you want!
hope this helped.

Expose a ViewModel event for binding to a custom DependencyProperty

Is it possible to expose a public event from my ViewModel is such a way as to allow it to be bound to a custom DependencyProperty in my View?
My application is written in C# using the .NET 4.5 framework. It has a MVVM architecture with no code-behind in the view and custom DependencyProperty classes to bind WPF-specific behvaiours of the View to properties exposed by the ViewModel.
There is a set of properties that I would like the ViewModel to be able to expose that represent events to which the View needs to respond. For example, when a top level ViewModel object is about to be Disposed I would like the WPF View implementation to respond by closing the corresponding Window. This could occur when a configuration process has displayed a Dialog Window, the user has enetered and confirmed the information and the ViewModel has passed it to the Model and is no longer required.
I am aware that there are many questions that are specific to solving the 'show dialog from ViewModel' question; this is not one of them and I have a solution to that one.
I've read through the MSDN documentation for DependencyProperties and can't find anything specific to binding to event properties.
What I would like to achieve is something similar to the code below. This code builds, but results in a typical System.Windows.Data Error: 40 : BindingExpression path error: 'RequestCloseEvent' property not found error when the MainWindow is shown.
I am aware that there are many questions that go along the lines of 'please help me debug my System.Windows.Data Error: 40 issue'; this is (probably) not one of these either. (But I'd be happy if that's all it really is.)
Source for the custom DependencyProperty in WindowBindableProperties.cs:
using System;
using System.Threading;
using System.Windows;
namespace WpfEventBinding
{
public static class WindowBindableProperties
{
#region ViewModelTerminatingEventProperty
/// <summary>
/// Register the ViewModelTerminatingEvent custom DependencyProperty.
/// </summary>
private static DependencyProperty _viewModelTerminatingEventProperty =
DependencyProperty.RegisterAttached
(
"ViewModelTerminatingEvent",
typeof(ViewModelTerminatingEventHandler),
typeof(WindowBindableProperties),
new PropertyMetadata(null, ViewModelTerminatingEventPropertyChanged)
);
/// <summary>
/// Identifies the ViewModelTerminatingEvent dependency property.
/// </summary>
public static DependencyProperty ViewModelTerminatingEventProperty
{ get { return _viewModelTerminatingEventProperty; } }
/// <summary>
/// Gets the attached ViewModelTerminatingEvent dependecy property.
/// </summary>
/// <param name="dependencyObject">The window attached to the WindowViewModel.</param>
/// <returns>The ViewModelTerminatingEventHandler bound to this property</returns>
public static ViewModelTerminatingEventHandler GetViewModelTerminatingEvent
(DependencyObject dependencyObject)
{
return (dependencyObject.GetValue(ViewModelTerminatingEventProperty)
as ViewModelTerminatingEventHandler);
}
/// <summary>
/// Sets the ViewModelTerminatingEvent dependency property.
/// </summary>
public static void SetViewModelTerminatingEvent(
DependencyObject dependencyObject,
ViewModelTerminatingEventHandler value)
{
dependencyObject.SetValue(ViewModelTerminatingEventProperty, value);
}
/// <summary>
/// Gets the ViewModelTerminatingEvent dependency property.
/// </summary>
private static void ViewModelTerminatingEventPropertyChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
Window instance = d as Window;
if (null != instance)
{
if (null != e.OldValue)
{
throw new System.InvalidOperationException(
"ViewModelTerminatingEvent dependency property cannot be changed.");
}
if (null != e.NewValue)
{
// Attach the Window.Close() method to the ViewModel's event
var newEvent = (e.NewValue as ViewModelTerminatingEventHandler);
newEvent += new ViewModelTerminatingEventHandler(() => instance.Close());
}
}
}
#endregion
}
}
Source for MainWindow.xaml:
(This example contains code-behind to simplify the Stop Button implementation.)
<Window x:Class="WpfEventBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="clr-namespace:WpfEventBinding"
v:WindowBindableProperties.ViewModelTerminatingEvent="{Binding Path=RequestCloseEvent}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="{Binding Path=CloseCommandName}" Click="StopButton_Click" ></Button>
</Grid>
</Window>
Source for MainWindow.xaml.cs (code behind):
using System.Windows;
namespace WpfEventBinding
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void StopButton_Click(object sender, RoutedEventArgs e)
{
MainWindowViewModel vm = (DataContext as MainWindowViewModel);
if (null != vm)
{
vm.Stop();
}
}
}
}
Source for the MainWindowViewModel.cs:
using System;
using System.ComponentModel;
namespace WpfEventBinding
{
public delegate void ViewModelTerminatingEventHandler();
class MainWindowViewModel
: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// Raised by the ViewModel to indicate to the view that it is no longer required.
// Causes System.Windows.Data Error: 40 : BindingExpression path error. Is it
// Possible to bind to an 'event' property?
public event ViewModelTerminatingEventHandler RequestCloseEvent;
// This has to have the public 'get' to allow binding. Is there some way to
// do the same thing for the 'event'?
public String CloseCommandName { get; private set; }
public MainWindowViewModel()
{
CloseCommandName = "Close";
}
internal void Stop()
{
ViewModelTerminatingEventHandler RaiseRequestCloseEvent =
RequestCloseEvent;
if (null != RaiseRequestCloseEvent)
{
RaiseRequestCloseEvent();
}
}
internal void Start()
{
OnPropertyChanged("CloseCommandName");
OnPropertyChanged("ViewModelTerminatingEvent");
}
private void OnPropertyChanged(String propertyName)
{
PropertyChangedEventHandler RaisePropertyChangedEvent = PropertyChanged;
if (RaisePropertyChangedEvent != null)
{
var propertyChangedEventArgs = new PropertyChangedEventArgs(propertyName);
RaisePropertyChangedEvent(this, propertyChangedEventArgs);
}
}
}
}
Source for App.xaml:
<Application x:Class="WpfEventBinding.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Application.Resources>
<!-- Nothing to see here. Move along... -->
</Application.Resources>
</Application>
Source for App.xaml.cs
using System.Windows;
namespace WpfEventBinding
{
public partial class App : Application
{
public App()
{
Startup += new StartupEventHandler(App_Startup);
}
void App_Startup(object sender, StartupEventArgs e)
{
MainWindowViewModel vm = new MainWindowViewModel();
MainWindow window = new MainWindow();
// Make sure this is set before attempting binding!
window.DataContext = vm;
vm.Start();
window.Show();
}
}
}
It appears that the public event ViewModelTerminatingEventHandler RequestCloseEvent; syntax is not sufficient to allo the data binding to occur. A similar problem is see if the public String CloseCommandName { get; private set; } is declared as public String CloseCommandName; without the { get; private set; }. However, there is no { get; private set; } for events, which use the {add{} remove{}} syntax (and that does not solve the problem either).
Is what I'm attempting possible and if so, what have I missed?
View closing means window closing event. So you basically want react on events in the view. I read recently this arcticle, there was a very good image
and also mentioned EventBehavior existence.
Your best bet, if you don't want any code behind, is to use behaviors. Behavior is a simple attached property, which can perform actions, to example rising application-wide commands, which ViewModel can then catch without MVVM issues.
Here is an example of behavior:
public static class FreezeBehavior
{
public static bool GetIsFrozen(DependencyObject obj)
{
return (bool)obj.GetValue(IsFrozenProperty);
}
public static void SetIsFrozen(DependencyObject obj, bool value)
{
obj.SetValue(IsFrozenProperty, value);
}
public static readonly DependencyProperty IsFrozenProperty =
DependencyProperty.RegisterAttached("IsFrozen", typeof(bool), typeof(FreezeBehavior), new PropertyMetadata(OnIsFrozenChanged));
private static void OnIsFrozenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
var freezable = d as Freezable;
if (freezable != null && freezable.CanFreeze)
freezable.Freeze();
}
}
}
it's used like this
<DropShadowEffect ShadowDepth="2" local:FreezeBehavior.IsFrozen="True"/>
It can be attached to any freezable to freeze it. In your case you want to subscribe to event and invoke command or set property, or whatever to inform ViewModel.
What you are asking for is kinda weird, but I'm not going to get into a big long discussion about that....
You don't bind to events - you expose them and the view can add handlers for the events.
Of course this means you will have to put some code behind into the view - but this is fine provided it is UI related. To complete the decoupling your view should only handle the viewmodel as an interface, this means you can easily swap out viewmodels at a later stage.
(Note that I've avoided talking about event triggers).

Binding label to a variable

I am just starting with WPF and I am trying to setup binding between a local variable and a label. Basicaly I want to update the label when local variable changes. I was searching for solution but they all just use textbox as a source not just class variable and I am not even sure it works this way. So here is my code.
public partial class MainWindow : Window
{
int idCounter;
public MainWindow()
{
InitializeComponent();
Binding b = new Binding();
b.Source = idCounter;
b.Mode = BindingMode.OneWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
myLabel.SetBinding(Label.ContentProperty,b);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
idCounter++;
}
}
Button does work, idCounter changes value, but it does not update in label so I guess binding is wrong. Can someone tell me what is wrong? Thanks
Your code will work if you change your class to this...
public partial class Window1 : Window, INotifyPropertyChanged
{
private int _idCounter;
public int IdCounter
{
get { return _idCounter; }
set
{
if (value != _idCounter)
{
_idCounter = value;
OnPropertyChanged("IdCounter");
}
}
}
public Window1()
{
InitializeComponent();
myLabel.SetBinding(ContentProperty, new Binding("IdCounter"));
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
e.Handled = true;
IdCounter++;
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
Some of the issues you were having are...
The window itself should implement INotifyPropertyChanged so that the binding engine can place an ear on it.
the IdCounter needs to be public and have a public getter on it so that the binding engine can 'get' it.
You should set the DataContext to whatever class has declared IdCounter (the MainWindow in this case). Part of the problem was that the binding engine had no DataContext.
The BindingMode setting was a red-herring since a Label binds that way by default.
The UpdateSourceTrigger was a red-herring since the content of the label does not have a mechanism to update the source property. A label's content is not like a text box where the user can type something that the code needs to know about. When you're binding to something that the user cannot change, forget about UpdateSourceTrigger, it's the Target property that counts.
The handler should mark the event. This is good practice and did not affect the binding.
The binding constructor needs only the path.
This code will give you your expected result; i.e., that the label updates when the button is clicked. Checked, compiled, and executed on vs2013, .net 4.5.
The other respondents said you should use a View Model. I agree with this 100%, and overall it's a good thing to consider.
You want to use a property to do this, as well as implementing INotifyPropertyChanged so that the label's content gets updated when the property changes.
Here's an example using a simple ViewModel
xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:converters="clr-namespace:WpfApplication1"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Label Width="200" Height="50" Content="{Binding MyLabel}"/>
<Button Height="30" Width="100" Content="Increment" Click="Button_Click" />
</StackPanel>
</Window>
xaml.cs:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
MainViewModel vm = new MainViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = vm;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
vm.MyLabel += 1;
}
}
}
MainViewModel.cs:
namespace WpfApplication1
{
public class MainViewModel : INotifyPropertyChanged
{
#region Members
private int _myLabel;
#endregion Members
#region Properties
public int MyLabel
{
get
{
return _myLabel;
}
set
{
_myLabel = value;
NotifyPropertyChanged("MyLabel");
}
}
#endregion Properties
public MainViewModel()
{
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
Note: Ideally, you would want to use a Command for the Button instead of a Click event handler
You cannot bind to something that is private or a field so convert it into public property. You can find more as to what is a valid binding source here
If you want changes to your property be picked up by UI you should implement INotifyPropertyChanged interface and raise event each time value of the property changes. So idCounter should look more like this:
private int _idCounter;
public int idCounter
{
get { return _idCounter; }
set
{
if (_idCounter != value)
{
_idCounter = value;
OnPropertyChanged("idCounter");
}
}
}
When you create binding to property you use Path
Binding works in binding context so you need to specify from where to take this Path. Easiest way to do that is to set DataContext. So in your case initialization should look more like this:
Binding b = new Binding("idCounter");
b.Mode = BindingMode.OneWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
myLabel.SetBinding(Label.ContentProperty, b);
DataContext = this;
As #d.moncada suggested in his answer you should create dedicated view model

Categories

Resources