C# WPF Update Textbox on Property change - c#

I got an Problem with updating the text in a Textbox. I got this MainWindow:
<Window x:Class="TestDatabinding.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>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10,10,10,10"/>
<Button Grid.Row="1" Content="Click me" Margin="10,10,10,10" Click="Button_Click"></Button>
<Button Grid.Row="2" x:Name="a1" Content="ShowText" Margin="10,10,10,10" Click="a1_Click" ></Button>
</Grid>
Now the cs-file for this MainWindow looks like:
using System.Windows;
namespace TestDatabinding
{
public partial class MainWindow : Window
{
MainWindowViewModel mwvm;
public MainWindow()
{
InitializeComponent();
mwvm = new MainWindowViewModel();
this.DataContext = mwvm;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
mwvm.ChangeText();
this.DataContext = mwvm;
}
private void a1_Click(object sender, RoutedEventArgs e)
{
mwvm.showText();
}
}
}
And last but not least the ViewModel Class:
using System.ComponentModel;
using System.Windows;
namespace TestDatabinding
{
class MainWindowViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private string text;
public string Text
{
get { return this.text; }
set
{
this.text = value;
OnPropertyChanged("Text");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public void ChangeText()
{
this.Text = "Hey paadddyy";
}
public void showText()
{
MessageBox.Show(Text);
}
}
}
I didn´t implement ICommands, because this is a simple test.
Now the Button's work correctly but the Textbox Text didn´t get updated.
Any suggestions what i can do? I only want to display "Hey paadddyy" when I click the first Button. After I press the second Button and then the first the MessageBox shows "Hey paadddyy" but the Textbox text didn´t get updated :(
Thank you for every hint ;)

Your MainWindowViewModel does not implement INotifyPropertyChanged. It needs to look like that:
class MainWindowViewModel: INotifyPropertyChanged
you define the event but does not implement the interface

It need to implement INotifyPropertyChanged
I suggested that if you want to do something with Notify Property. Another easy way is to apply Caliburn.Micro Framework to your project.
Follow this link.

Related

Validate Input of UserControl in Window WPF

Currently I have a UserControl contained within a window. The UserControl is made up of two text boxes. The UserControl is an element in my MainWindow. Outside the scope of my UserControl is my submit button in my window. I would like to enable and disable the button whenever the boxes text contents are not null or null.
UserControl XAML code:
<UserControl x:Class="myClass.myUserControl"
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">
<StackPanel Background="White">
<DockPanel>
<Label x:Name="lbl1" Content="First Box:"/>
<TextBox x:Name="txtbox1"/>
<Label x:Name="lbl1" Content="Second Box:"/>
<TextBox x:Name="txtbox2"/>
</DockPanel>
</StackPanel>
</UserControl>
View Model:
using System;
namespace myClass {
partial class UserControlViewModel: ViewModelBase {
private bool _validInput;
public UserControlViewModel() {
validInput = false;
}
public object validInput {
get { return _validInput; }
set {
_validInput = value;
OnPropertyChanged("validInput");
}
}
}
ViewModelBase:
using System.ComponentModel;
namespace myClass {
class ViewModelBase : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
My issue is deciding on how to handle this validation, my button's isEnabled property is currently bounded to the validInput boolean of the view model. However, the contents of the user control are not accessible in my window as I have abstracted it as a separate userControl item (I plan on having different user controls available to be shown in the window).
MainWindow XAML:
<Window x:Class="myClass.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:myClass"
Title="MainWindow" Height="356" Width="699" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<uc:UserControlViewModel/>
</Window.DataContext>
<Grid>
<UserControl x:Name="usrControl"/>
<Button x:Name="btn" Content="Create" Click="btn_Click" IsEnabled = "{Binding validInput}"/>
</Grid>
</Window>
MainWindow C#:
using System;
using System.Windows;
using System.Windows.Controls;
namespace myClass {
public partial class MainWindow: Window {
UserControlViewModel view;
public MainWindow() {
InitializeComponent();
view = new UserControlViewModel();
DataContext = view;
}
}
I need to be able to check the contents of the text boxes in the UserControl from the MainWindow as my view is in the MainWindow, however the contents are inaccessible to me and it doesn't make sense to have the view in the UserControl. How should I go about solving this?
I've created a similar project. Mainly to do this, validate through your c# code. Basically
(i don't remember id its .content or .text to get the value)
if(txtbox1.Content == ---or--- (textbox1.Content).Equals(Whatever)){
----code---
}
else{
MessageBox.Show("Error")
}
instead of 'disabling' the button (which I don't think you can do) just make it so if invalid, the user knows or just doesn't do anything.
unrelated: if you are wanting a certain input instead of a blank textbox input, you could use this code to give a base if user leaves empty
private void txtbox1_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (txtbox1.Text.Equals("your origional text"))
{
Name_Text.Text = "";
}
}
private void txtbox1_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (Name_Text.Text.Equals(""))
{
Name_Text.Text = "your origional text";
}
}
hope this helps

Raise a event on the User control from the Main Window in WPF

There is a textbox in my mainwindow.xaml, when I enter the textbox, I expect the label in my usercontrol, known as View1.xaml will be update accordingly. However I realise the event is not raise at all in the user control when I type the textbox, can you tell me which part is wrong?
The event is able to raise in TextBox_TextChanged_1
my MainWindow.XAML
<Window xmlns:my="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:testapplication" x:Class="testapplication.MainWindow"
Title="MainWindow" Height="964" Width="790">
<Grid >
<Button x:Name="OpenView1" Content="Open Window 1" HorizontalAlignment="Left" Margin="33,70,0,0" VerticalAlignment="Top" Width="111" RenderTransformOrigin="0.279,1.409" Click="OpenView1_Click"/>
<Button x:Name="OpenView2" Content="Open Window 2" HorizontalAlignment="Left" Margin="33,169,0,0" VerticalAlignment="Top" Width="111" Click="OpenView2_Click"/>
<Button x:Name="OpenView3" Content="Open Window 3" HorizontalAlignment="Left" Margin="33,259,0,0" VerticalAlignment="Top" Width="111" Click="OpenView3_Click"/>
<local:View1 x:Name="ViewOne" HorizontalAlignment="Left" Margin="33,332,0,0" VerticalAlignment="Top" Height="226" Width="204" Visibility="Hidden"/>
<local:View2 x:Name="ViewTwo" HorizontalAlignment="Left" Margin="284,332,0,0" VerticalAlignment="Top" Height="226" Width="208" Visibility="Hidden"/>
<local:View3 x:Name="ViewThree" HorizontalAlignment="Left" Margin="534,332,0,0" VerticalAlignment="Top" Height="226" Width="196" Visibility="Hidden"/>
<TextBox HorizontalAlignment="Left" Height="42" Margin="326,70,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="182" FontSize="22" TextChanged="TextBox_TextChanged_1"/>
</Grid>
</Window>
my MainWindow.cs
namespace testapplication
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
//InitializeComponent();
}
//event handler
public event EventHandler<EventArgs> changedText;
private void OpenView1_Click(object sender, RoutedEventArgs e)
{
ViewOne.Visibility = Visibility.Visible;
}
private void OpenView2_Click(object sender, RoutedEventArgs e)
{
ViewTwo.Visibility = Visibility.Visible;
}
private void OpenView3_Click(object sender, RoutedEventArgs e)
{
ViewThree.Visibility = Visibility.Visible;
}
private void TextBox_TextChanged_1(object sender, TextChangedEventArgs e)
{
if (changedText != null)
{
changedText(this, e);
}
}
}
}
This is my UserControl, known as View1.xaml, it is included in my MainWindow.Xaml
namespace testapplication
{
/// <summary>
/// Interaction logic for View1.xaml
/// </summary>
public partial class View1 : UserControl
{
private MainWindow newWindow = new MainWindow();
public View1()
{
InitializeComponent();
newWindow.changedText += newWindow_ChangeText;
}
void newWindow_ChangeText(object sender, EventArgs e)
{
ViewOnelabel.Content = "Happy";
}
}
}
The problem is my ViewOnelabel.Content = "Happy" did not execute at all, it remain unchanged
There are a few things I would like to point out.
The equivalent of a winforms label in wpf is a TextBlock. A wpf label is actually a type of contentcontrol. Hence the content property.
In wpf there are routed events. These "bubble" up ( and tunnel down ) the visual tree. That means you can handle an event in the window from a control in a usercontrol inside it.
But mainly.
I encourage you to look into the MVVM pattern.
I've put together some code which illustrates these points.
I'd recommend just using binding and mvvm though.
My MainWindow markup:
Title="MainWindow" Height="350" Width="525"
TextBoxBase.TextChanged="Window_TextChanged"
>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<Label Name="OutputLabel"/>
<TextBlock Text="{Binding OutputString}"/>
<local:UserControl1/>
</StackPanel>
</Grid>
Notice that it handles a textchanged event and because that's routing it will get the event from UserControl1 inside it.
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_TextChanged(object sender, TextChangedEventArgs e)
{
OutputLabel.Content = $"Happy {((TextBox)e.OriginalSource).Text}";
}
}
You don't do anything with the text from your textbox in your handler but I have some code there proves you could get at that from mainwindow if you wanted.
My viewmodel:
public class MainWindowViewModel : INotifyPropertyChanged
{
private string inputString;
public string InputString
{
get { return inputString; }
set
{
inputString = value;
OutputString = $"{inputString.Length} characters entered";
RaisePropertyChanged();
}
}
private string outputString;
public string OutputString
{
get { return outputString; }
set
{
outputString = value;
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Usercontrol1 just has a textbox:
<Grid>
<TextBox Text="{Binding InputString, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
As you type in that textbox, the text is transferred to the bound property in my viewmodel. That hits the code in my setter. This in turn sets OutputString which is bound to my textblock.
Text changes in both my label and textblock as I type.
Here's a link to my sample on onedrive
https://1drv.ms/u/s!AmPvL3r385QhgpgOPNKPs-veFJ2O3g
The main problem here is that your View1 class is subscribing to an event on a new MainWindow instance, not the MainWindow instance created by your application on start.
Since your MainWindow class has a reference to your View1 class (a named member "ViewOne") you should just change it from the MainWindow class.
private void TextBox_TextChanged_1(object sender, TextChangedEventArgs e)
{
ViewOne.ViewOneLabel.Content = "Happy";
}
Get rid of the chenagedText event handler and all the code in the View1.xaml.cs... you don't need it.
Note: I am hoping that you are just playing around and learning here... there is no way I would condone building a WPF application in this way.
You could only use the event of the MainPage. I recomment you to add a Property to the UserControl. In my case I call it Text.
public string Text
{
set { ViewOneLabel.Content = value; }
}
In the MainWindow use the Property within the TextChanged Event.
private void TextBox_TextChanged_1(object sender, TextChangedEventArgs e)
{
OpenView1.Text = TextBox.Text;
}
You are creating a new instance of MainWindow in your UserControl. What you want to do is to hook up an event handler to the instance that you actually see on the screen. You can get a reference to this one using the Window.GetWindow method:
public partial class View1 : UserControl
{
public View1()
{
InitializeComponent();
Loaded += (s, e) =>
{
Window mainWindow = Window.GetWindow(this) as MainWindow;
if(mainWindow != null)
mainWindow.changedText += newWindow_ChangeText;
};
}
void newWindow_ChangeText(object sender, EventArgs e)
{
ViewOnelabel.Content = "Happy";
}
}

WPF MVVM ErrorCode: CS1061

- the red markups are for Inputs(xAxis and yAxis)
- after i press the button (Calculate), the inputs should be calculated.
- then outputed at the blue markup(texbox called Output)
After i made some kata with element binding, i just wanted to
start MVVM property binding.
For some reason i get a ErrorCode: CS1061
Error CS1061 MainWindow does not contain a definition for
CalculateClick and no extension method CalculateClick accepting a
first argument of type MainWindow could be found (are you missing a
using directive or an assembly reference?)
the weird part of this is when i use the resharper eventhandler on my MainWindow.xml at my button it creates a event in my MainWindow.cs.
But it didnt before. the events were autocreated in my ViewModel.cs
im not sure what causes this error
ty in advance when somone could help me, i have already been sitting on this kata for more then 8 hours.
Heres my MainWindow.xml:
<Window x:Class="Coordinates.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Coordinates"
mc:Ignorable="d"
Title="MainWindow" Height="250" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Insert X-Coordiante"/>
<Label Grid.Row="0" Grid.Column="1" Content="Insert Y-Coordinate"/>
<TextBox Grid.Row="1" Grid.Column="0" Name="TxtXaxis" Text="{Binding Xaxis}"/>
<TextBox Grid.Row="1" Grid.Column="1" Name="TxtYaxis" Text="{Binding Xaxis}"/>
<TextBox Grid.Row="2" Grid.Column="0" Text="{Binding Output}"/>
<Button Grid.Row="2" Grid.Column="1" Name="Calculate" Click="CalculateClick">Calculate</Button>
</Grid>
This is my MainWinow.xml.cs:
using System.Windows;
namespace Coordinates
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.InitializeComponent();
this.DataContext = new ViewModel();
}
}
}
This is my ViewModel.cs:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace Coordinates
{
public class ViewModel : INotifyPropertyChanged
{
#region Inputs
private double _xaxis;
public double Xaxis
{
get => this._xaxis;
set
{
if (value == this._xaxis)
{
return;
}
this._xaxis = value;
this.OnPropertyChanged();
}
}
private double _yaxis;
public double Yaxis
{
get => this._yaxis;
set
{
if (value == this._yaxis)
{
return;
}
this._yaxis = value;
this.OnPropertyChanged();
}
}
#endregion
public void CalculateClick(object sender, RoutedEventArgs e)
{
Output = Math.Sqrt(Math.Pow(Xaxis,2)+Math.Pow(Yaxis,2));
}
private double _output;
public double Output
{
get => this._output;
set
{
if (value == this._output)
{
return;
}
this._output = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
EDIT: this was missing in CalculateClick in my viewmodel:
Output = Math.Sqrt(Math.Pow(Xaxis,2)+Math.Pow(Yaxis,2));
You are using a CalculateClick as Eventhandler for the Click Event of the Button.
Per Default, WPF looks in the Code Behind (MainWindow.cs) for the Eventhandler, which is not there.
Since you are already initializing your DataContext within the Code Behind. Make your ViewModel a private member of your MainWindow.
Create an event handler in the MainWindow Code Behind and call your event handler in the ViewModel from there.
public partial class MainWindow : Window
{
private _viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
this.InitializeComponent();
this.DataContext = _viewModel;
}
}
public void CalculateClick(object sender, RoutedEventArgs e)
{
_viewModel.CalculateClick(Sender, e);
}
You might also want to look into Commands, which can be created directly inside of the ViewModel and then be bound to. There is an ActionCommand or GenericCommand, which take an Action or Action<T> and are pretty easy to use.

Databinding and then calling from another class in WPF

My purpose is to add a textblock to my main UI window, of which text will be updated if needed. For that, in my UIWindow xaml I did like this:
<Window x:Class="UIDesigner.UIWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:UIDesigner"
xmlns:c="clr-namespace:UIDesigner.Controls"
WindowStartupLocation="CenterScreen"
WindowState="Maximized"
WindowStyle="SingleBorderWindow"
Title="GUI"
Height="1000" Width="1400"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Icon="Resources/Images/Logo.png"
>
<Grid Margin="0">
<Grid Grid.Row="1" Margin="0,10,0,0">
<GroupBox Header="Console" Grid.Column="1" Margin="0,590,0,0" HorizontalAlignment="Stretch" x:Name="consoleWindow" IsEnabled="True" VerticalAlignment="Stretch"
>
<TextBlock x:Name="myConsoleWindowTextBlock" Text="{Binding Path=consoleText}"/>
</GroupBox>
</Grid>
</Grid>
</Window>
This is the code behind:
using System.Windows;
using System.Runtime.CompilerServices;
using System.ComponentModel;
namespace UIDesigner
{
public partial class UIWindow : Window
{
public UIWindow()
{
InitializeComponent();
}
private string _consoleText;
public string consoleText
{
get{ return _consoleText;}
set
{
_consoleText = value;
NotifyPropertyChanged("consoleText");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
Then in my main class, I call this UIWindow like this:
namespace UIDesigner
{
public partial class Main : Window
{
public Main()
{
InitializeComponent();
}
private void LoginButton_Click_1(object sender, RoutedEventArgs e)
{
var myUIWindow = new UIWindow();
myUIWindow.PropertyChanged += new PropertyChangedEventHandler(UIWindow_PropertyChanged);
myUIWindow.consoleText = "Hello User!";
myUIWindow.ShowDialog();
this.Close();
}
private void LoginButton_MouseEnter_1(object sender, MouseEventArgs e)
{
}
static void UIWindow_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
MessageBox.Show("Something Changed!");
MessageBox.Show(e.PropertyName);
}
}
}
Now I have two problems here:
First, when my UI window starts, I indeed received two message boxes, saying "something changed" followed by "consoleText". So that means the consoleText is changed successfully. But after my UIWindow shows up, the textblock is empty, I cannot see "Hello User!" there. Seems like Text="{Binding Path=consoleText} part is not working correctly in my xaml file.
Second and most importantly, I want to change the consoleText in another different class, namely in DesignerCanvas.Commands.cs. For that I couldn't figure out any solution. I want something like this in my DesignerCanvas.Commands.cs:
namespace UIDesigner
{
public partial class DesignerCanvas
{
private void changeConsoleOutput(string updatedConsoleText)
{
myUIWindow.consoleText = updatedConsoleText; //obviously, this is not working
}
}
}
Any kind of suggestion will be much appreciated.
1.First of two set the value in UI just add below one line
in constructor of UIWindow class
this.DataContext=this;
//because only specifying property consoletext, it will not able to know where to find consoletext.
2.u can find that UIwindow in App.Current.Windows and cast it to UIWindow type and then can
access the property.
foreach(Window win in App.Current.Windows)
{
if (win as UIWindow != null)
{
(win as UIWindow).consoletext = updatedConsoleText;
}
}
For second problem
Change
<TextBlock x:Name="myConsoleWindowTextBlock" Text="{Binding Path=consoleText}"/
To
<TextBlock x:Name="myConsoleWindowTextBlock" Text="{Binding Path=.}"/
and
in UIWindow constructor set
myConsoleWindowTextBlock.Datacontext=consoleText;

Simple databinding not working

I'm new to WPF and I'm trying to make a simple app, a stopwatch. It works fine if I'm not doing the data binding. Here's my XAML.
<Window x:Class="StopWatch.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:StopWatch"
Title="MainWindow" Height="318" Width="233">
<Window.Resources>
<s:StopWatchViewModel x:Key="swViewModel" x:Name="swViewModel"></s:StopWatchViewModel>
</Window.Resources>
<Grid DataContext="{StaticResource swViewModel}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="128*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="1" Height="49" HorizontalAlignment="Left" Margin="42,50,0,0" Name="txtTime" Text="{Binding Path=Message}" VerticalAlignment="Top" Width="147" FontSize="20" TextAlignment="Center" />
<Button Content="Start" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="12,15,0,0" Name="startBtn" VerticalAlignment="Top" Width="58" Click="startBtn_Click" />
<Button Content="Stop" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="76,15,0,0" Name="stopBtn" VerticalAlignment="Top" Width="58" Click="stopBtn_Click" />
<Button Content="Reset" Grid.Row="2" Height="23" HorizontalAlignment="Left" Margin="140,15,0,0" Name="resetBtn" VerticalAlignment="Top" Width="59"/>
</Grid>
and here is the code in MainWindow
public partial class MainWindow : Window
{
private StopWatchViewModel stopwatch;
public MainWindow()
{
InitializeComponent();
stopwatch = new StopWatchViewModel();
}
private void startBtn_Click(object sender, RoutedEventArgs e)
{
stopwatch.Start();
}
private void stopBtn_Click(object sender, RoutedEventArgs e)
{
stopwatch.Stop();
}
}
and here's the code in StopWatchViewModel.cs
class StopWatchViewModel : INotifyPropertyChanged
{
private DispatcherTimer timer;
private Stopwatch stopwatch;
private string message;
public event PropertyChangedEventHandler PropertyChanged;
public string Message
{
get
{
return message;
}
set
{
if (message != value)
{
message = value;
OnPropertyChanged("Message");
}
}
}
public StopWatchViewModel()
{
timer = new DispatcherTimer();
stopwatch = new Stopwatch();
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
stopwatch.Reset();
}
public void Start()
{
stopwatch.Start();
}
public void Stop()
{
stopwatch.Stop();
}
private void timer_Tick(object sender, EventArgs e)
{
Message = stopwatch.Elapsed.ToString(); // Doesn't work.
// Message = "hello"; does not work too!
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I don't know where I got it wrong.
EDIT: I got it working. So here's the working code for anyone's reference.
XAML, change the original to this
<Window x:Class="StopWatch.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:StopWatch"
Title="MainWindow" Height="318" Width="233">
<Grid> // partial code
and in behind code, change the constructor based on Erno's suggestion.
public MainWindow()
{
InitializeComponent();
viewModel = new StopWatchViewModel();
this.DataContext = viewModel;
}
Thanks guys!
Your problem here is that you don't have any mechanism for letting WPF know that your property is updated. Basically you have two options here:
Make Message into a Dependancy Property.
Implement INotifyPropertyChanged so that it let's the GUI know when the message have been updated.
To make sure that you have all parts for getting INotifyPropertyChanged to work, check that you did all of this:
Define the event PropertyChanged.
Make a private NotifyPropertyChanged method to raise the event. This method should take a string parameter (name of the property) and raise the event like this : PropertyChanged(this, new PropertyChangedEventArgs(<nameofproperty>). The reason to make this method is to put the null check and invocation details in one place.
In the property setter, call NotifyPropertyChanged with the correct name (case-sensitive) of the property after the value has changed.
Just replace your Message property like this and it will work:
public string Message
{
get { return (string)GetValue(MessageProperty); }
set { SetValue(MessageProperty, value); }
}
public static readonly DependencyProperty MessageProperty =
DependencyProperty.Register("Message", typeof(string),
typeof(MainWindow), new UIPropertyMetadata(String.Empty));
EDIT after posting my solution I noticed you changed all the code into a ViewModel solution... feel free to ignore or go back to your first set of code.
In your new code you are creating TWO instances of the ViewModel, one in code and one in the resources. That is not good as you are manipulating the one in code and binding to the one in the resources(xaml)
EDIT:
change your constructor to this:
public MainWindow()
{
InitializeComponent();
stopwatch = new StopWatchViewModel();
this.DataContext = stopwatch;
}
That's all
Changes to the property won't get noticed if you don't implement INotifyPropertyChanged correctly. So you should do that first. Maybe you missed something when you did it earlier.

Categories