I'm a beginner in wpf. I am following a textbook to learn, the examples are shown but its not working whenever I am writing dynamic binding part even after following each of its instructions strictly thrice.
this is the code behind
using System;
using System.ComponentModel;
namespace KarliCards_GUI
{
[Serializable]
public class GameOptions:INotifyPropertyChanged
{
private bool _playAgainstComputer = false;
public bool PlayAgainstComputer
{
get
{
return _playAgainstComputer;
}
set
{
_playAgainstComputer = value;
OnPropertyChanged("PlayAgainstComputer");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In XAML file I want to dynamically bind IsChecked property of a checkbox by using DataContext which will have an instance of GameOptions.
Below part of code is in XAML file
<CheckBox Content="Play against computer" HorizontalAlignment="Left" Margin="11,33,0,0" VerticalAlignment="Top" Name="playAgainstComputerCheck" IsChecked="{Binding Path=PlayAgainstComputer}" />
and below is the code of csharp file of that XAML
namespace KarliCards_GUI
{
public partial class Options : Window
{
private GameOptions _gameOptions;
public Options()
{
if (_gameOptions == null)
{
if (File.Exists("GameOptions.xml"))
{
using (var stream = File.OpenRead("GameOptions.xml"))
{
var serializer = new XmlSerializer(typeof(GameOptions));
_gameOptions = serializer.Deserialize(stream) as GameOptions;
}
}
else
_gameOptions = new GameOptions();
}
DataContext = _gameOptions;
InitializeComponent();
}
}
}
The problem I am facing is, in the property 'PlayAgainstComputer', in set part if I set the variable(_playAgainstComputer) as 'value' it is always checked in the checkbox.
Might be the case the since the InitializeComponent() method is being called after the DataContext is set.
The default value for IsChecked property of CheckBox is true. I suggest first to call the InitializeComponent() method and then set the DataContext.
Related
I have had some problems with data bindings in WPF, so I have been playing around to try to figure out what is going on. But I ran into something that I do not understand, and I hope someone could explain it to me. The code below is not anything I'm trying to use, it is only for testing.
I have a simple class "Lamp" with only one string property "Name". I also override ToString(), so that it returns the name.
In a "ViewModel" class I create a "Lamp" property and a ICommand:
class ViewModel : ViewModelBase
{
private Lamp _lamp1;
public Lamp Lamp1
{
get { return _lamp1; }
set { _lamp1 = value; }// OnPropertyChanged("Lamp1"); }
}
public ICommand Lamp_click { get { return new RelayCommand(param => LampClickExecute(param)); } }
public ViewModel()
{
Lamp1 = new Lamp() { Name = "Test" };
}
private void LampClickExecute(object param)
{
var name = Lamp1.Name + "I";
//HERE IS THE QUESTION!
//Lamp1 = new Lamp() { Name = name };
Lamp1.Name = name;
OnPropertyChanged("Lamp1");
}
}
In the view, I only have a button that binds to the command, and a label that I'm binding to Lamp1:
<Button x:Name="btn_lamp" Content="Button" HorizontalAlignment="Left" Margin="859,27,0,0" VerticalAlignment="Top" Height="29" Command="{Binding Path= Lamp_click}" CommandParameter="{Binding Path=Lamp1 }"/>
<Label Content= "{Binding Lamp1}" HorizontalAlignment="Left" Margin="797,56,0,0" VerticalAlignment="Top"/>
If I in the command create a new instance of "Lamp" with a new name and call OnPropertyChanged (still in the command, it is commented away in the setter) everything is fine and the new value is shown in the view. But if I do not create a new instance, instead just changing the name of the current one, the view is not updated. I have put a breakpoint in the command to see that everytime the button is clicked, there is an "I" added to the name, nothing strange there.
What is going on behind the scenes here? Is it somehow required that the setter is called, eventhough OnPropertyChanged is called in the command?
As I said, I'm not trying to acheive anyhting specific with this code, just want this behaviour explained.
UPDATE:
My ViewModelBase looks like this:
class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
internal void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You should use the INotifyPropertyChanged and add the code to your ViewModelBase class to update any object on the View.
{
public class ViewModelBase: INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
}
}
And then your code should call the OnPropertyChanged
public Lamp Lamp1
{
get { return _lamp1; }
set { SetProperty(ref _lamp1, value; }
}
In your code you are not changing the property Lamp1. You are changing the property in the lamp class Name. If you implement INotifyPropertyChanged in you lamp class with the Name field it will update. With your current code if you changed Lamp1 to a new instance of Lamp with a different name then it would record the change because you are changing the Lamp1 field.
public class Lamp : NotifyChange { //NotifyChange is the INotifyPropertyChanged implementation in a base class
private _Name;
public Name{
get{ return _Name; }
set{
if( _Name != value ) {
_Name = value;
OnPropertyChanged( nameof( Name ) );
}
}
}
}
The property change needs to be implemented where the change is happening or it won't know that its changed. Hope that makes sense why you are not getting the update to show in your view.
I am having a binding issue I wasn't able to figure out for the past two days. I have thoroughly went through most of the relevant threads on SO, and I still wasn't able to pinpoint where my error lies.
The issue I'm having is with one of the textboxes in my program. The purpose of it is to show the file the user has selected from the file browser. I have bound the text property of it to a string called parameterFileSelected but the textbox never updates even though debugging seems to be showing that the iNotifyPropertyChanged is called and executed properly.
Please help me take a look at my code below if there are any mistakes in my code.
The textbox is part of an xaml called GenerateReports and this view is tied to the GenerateReportsViewModel as follows:
Code for setting datacontext to GenerateReportsViewModel
<Grid >
<Grid.DataContext>
<vm:GenerateReportsViewModel/>
</Grid.DataContext>
<Grid.ColumnDefinitions>
....
Code for TextBox. I have tried removing the Twoway mode, changing it to Oneway and removing the mode but there is no difference.
<TextBox Grid.Column="2" Grid.Row="1" Margin="5" Text="{Binding parameterFileSelected, Mode=Twoway, UpdateSourceTrigger=PropertyChanged}" ></TextBox>
To get the file browser and then to pass the selected file result to the GenerateReportsViewModel, this is the function in the codebehind file. The genviewmodel is initialized in the beginning of the codebehind file as GenerateReportsViewModel genViewModel = new GenerateReportsViewModel();
private void ParaFileButtonClick(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
{
DataContext = genViewModel;
genViewModel.updateParameterFileSelected(openFileDialog.FileName.ToString());
}
}
This is the code that's called in GenerateReportsViewModel to update the parameterFileSelected string the textbox is bound to.
class GenerateReportsViewModel : ViewModelBase
{
private string _parameterFileSelected;
public string parameterFileSelected
{
get { return _parameterFileSelected; }
set { SetValue(ref _parameterFileSelected, value); }
}
public void updateParameterFileSelected(string parameterFile)
{
parameterFileSelected = parameterFile;
}
}
Here is the ViewModelBase the viewmodel is attached to.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void SetValue<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
{
if (property != null)
{
if (property.Equals(value)) return;
}
OnPropertyChanged(propertyName);
property = value;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
EDIT
Working Solution after Applying Kevin's Suggestions
For simplicity sake, the Datacontext was set in the XAML.
<Grid>
<Grid.DataContext>
<vm:GenerateReportsViewModel x:Name="generateReportsViewModel"/>
</Grid.DataContext>
Then, I call the string the textbox was bound to, in the viewmodel directly from code behind.
private void ParaFileButtonClick(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
if (openFileDialog.ShowDialog() == true)
{
generateReportsViewModel.parameterFileSelected = openFileDialog.FileName.ToString();
}
}
The ViewModel now uses Kevin's ViewModelBase:
public class GenerateReportsViewModel : ViewModelBase
{
public string parameterFileSelected
{
get { return this.GetValue<string>(); }
set { this.SetValue(value); }
}
}
Thank you Kevin for your solution. Now my 2-day-long problem is solved.
I found out that my previous ViewModelBase was calling iNotifyPropertyChanged but somehow when the View was updated, the value was null instead.
I'm trying to understand why using the ref keyword in your viewModel. I learned a nice way to create the BaseViewModel from the Classon and Baxter book which you can find below. The view-model implements the INotifyPropertyChanged like you did. What you did with [CallerMemberName] is great, it's really magical the way we can reference to our properties thanks to it.
The view model uses a the dictionary to store its properties. It uses a pretty neat trick of looking through the dictionnary keys to see if we contain the string name of the property.Otherwise, we will return a default T value.
public class CommonBaseViewModel: INotifyPropertyChanged
{
private Dictionary<string, object> Values { get; set; }
protected CommonBaseViewModel()
{
this.Values = new Dictionary<string, object>();
}
public event PropertyChangedEventHandler PropertyChanged;
protected T GetValue<T>([CallerMemberName] string name=null)
{
if (this.Values.ContainsKey(name))
{
return (T)this.Values[name];
}
else
{
return default(T);
}
}
protected void SetValue(object value, [CallerMemberName] string name = null)
{
this.Values[name] = value;
//notify my property
this.OnPropertyChanged(new PropertyChangedEventArgs(name));
}
protected void OnPropertyChanged([CallerMemberName] string name=null)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(name));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if(this.PropertyChanged != null)
{
this.PropertyChanged(this, e);
}
}
}
As for your GenerateReportViewModel, with the common view model that I provided you, your class then becomes :
public class GenerateReportsViewModel : CommonViewModelBase
{
private string _parameterFileSelected;
public string parameterFileSelected
{
get { return _parameterFileSelected; }
set { SetValue(ref _parameterFileSelected, value); }
}
get
{
return this.GetValue<string>();
}
set
{
this.SetValue(value);
}
public void updateParameterFileSelected(string parameterFile)
{
parameterFileSelected = parameterFile;
}
}
Oh before I forgot, I don't know if it was your intention, but your GenerateReportViewModel is private. This has some impact on your code. Don't forget that by defaut, classes are private!
As for your code behind, even though it could be consider bad practice, I recommend that you have a private field (OpenFileDialog _openFileDialog)that you construct while initializing your page. Because doing it each time your clicking your button is going to consume more data that you need your application to.
//EDIT
I have review my code,and it seemed that the property was not programmed correctly.
public class GenerateReportsViewModel : CommonViewModelBase
{
private string _parameterFileSelected;
public string parameterFileSelected
{
get
{
return this.GetValue<string>();
}
set
{
this.SetValue(value);
}
public void updateParameterFileSelected(string parameterFile)
{
parameterFileSelected = parameterFile;
}
}
More about my comment about constructing the page and binding the view model. While creating your page, you have to create the view-model for that page and then bind it to the data context.
I don't know what you do in your code, but I could provide with this sample such as
public GenerateReportView()
{
InitializeComponent();
//Some operations
var generateReportViewModel = new GenerateReportViewModel();
this.DataContext = generateReportViewModel;
}
I've been working on this issue for a few days and can't seem to find anything that will work for my application.
My issue is that I am trying to use a User Control containing buttons to bind to commands which change the source Uri of a frame (both displaying in the same window). When I click a button it is changing the Uri within the ViewModel but the frame does not change the page to reflect this. I believe that it is either not picking up the change due to the way it is binding or there is something blocking it from changing the page which is displaying in the frame.
I am using the MVVM pattern which has been great until I reached the point that I had to start dealing with navigation. Any help would be greatly appreciated!
Navigation User Control View Buttons:
<Button Name="BtnMainDash" Content="Main Dashboard" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="180" Command="{Binding MainDashboard}"/>
<Button Name="BtnAccount" Content="Account" HorizontalAlignment="Left" Margin="10,40,0,0" VerticalAlignment="Top" Width="180" Command="{Binding EditAccount}"/>
<Button Name="BtnProjects" Content="Projects" HorizontalAlignment="Left" Margin="10,70,0,0" VerticalAlignment="Top" Width="180" Command="{Binding ProjectScreen}"/>
Main Window Frame:
<Frame x:Name="FmePages" Margin="200,30,-0.4,0.4"
Source="{Binding Path=CurrentPage, Mode=OneWay, UpdateSourceTrigger=PropertyChanged }"
NavigationUIVisibility="Hidden"/>
Button ICommands (All the same except that each calls a difference Uri changing command):
using System;
using System.Windows.Input;
using ScrumManagementApplication.Pages.MainWindow.ViewModel;
namespace ScrumManagementApplication.Pages.MainWindow.Commands
{
class LoadEditAccount : ICommand
{
private readonly NavigationViewModel _navigationViewModel;
public LoadEditAccount(NavigationViewModel navigationViewModel)
{
_navigationViewModel = navigationViewModel;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _navigationViewModel.CommandsEnabled;
}
public void Execute(object parameter)
{
_navigationViewModel.LoadEditAccount();
}
}
}
ViewModel:
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using ScrumManagementApplication.Pages.MainWindow.Commands;
using ScrumManagementApplication.SessionData;
using MessageBox = System.Windows.MessageBox;
namespace ScrumManagementApplication.Pages.MainWindow.ViewModel
{
public class NavigationViewModel : INotifyPropertyChanged, INotifyPropertyChanging
{
public bool CommandsEnabled = false;
public NavigationViewModel()
{
MainDashboard = new LoadMainDashboard(this);
EditAccount = new LoadEditAccount(this);
ProjectScreen = new LoadProjectScreen(this);
LogOut = new LoadLogOut(this);
CommandsEnabled = true;
LoadEditAccount();
}
#region ICommands
public ICommand MainDashboard { get; private set; }
public void LoadMainDashboard()
{
_currentPage = null;
_currentPage = new Uri("pack://application:,,,/Pages/MainWindow/View/MainDashboardView.xaml", UriKind.Absolute);
}
public ICommand EditAccount { get; private set; }
public void LoadEditAccount()
{
_currentPage = null;
_currentPage = new Uri("pack://application:,,,/Pages/EditUserDetailsPage/View/EditUserDetailsView.xaml", UriKind.Absolute);
}
public ICommand ProjectScreen { get; private set; }
public void LoadProjectScreen()
{
_currentPage = null;
_currentPage = new Uri("pack://application:,,,/Pages/ProjCreationPage/View/ProjectCreationPage.xaml", UriKind.Absolute);
}
public ICommand LogOut { get; private set; }
public void LoadLogOut()
{
var dialogResult = MessageBox.Show("Are you sure you want to log out?", "Log Out", MessageBoxButton.YesNo);
if (dialogResult == (MessageBoxResult) DialogResult.Yes)
{
App.Current.Shutdown();
}
}
#endregion // ICommands
#region MainFrame
private Uri _currentPage;
public Uri CurrentPage
{
get { return _currentPage; }
set
{
_currentPage = value;
OnPropertyChanged("CurrentPage");
}
}
#endregion // MainFrame
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion // INotifyPropertyChanged Members
public event PropertyChangingEventHandler PropertyChanging;
protected void OnPropertyChanging(String propertyName)
{
PropertyChangingEventHandler handler = PropertyChanging;
if (handler != null)
{
handler(this, new PropertyChangingEventArgs(propertyName));
}
}
}
}
Any & all help appreciated, even if it doesn't fully solve my issue anything that helps me get closer to a solution is good!
Thanks in advance
Instead of doing this
_currentPage = //some value;
do this:
CurrentPage = //some value
The event will be raised when you change the property and not the backing field.
EDIT
One more suggestion is to create a single command class since all your commands are setting a string value to a property. You can get the button name using the CommandParameter. Based on that set the Uri.
Got a similar Problem, had a mainwindow with a frame in it and when I, for example, opened an openfiledialog and closed it, this frame (some others too) did not update even if I called PropertyChanged() and the bound Property had the right value. The only solution for me was to remove the binding and handle the Content with NavigationService instead. If you need this Property you bound to for some other approaches you could do something like:
private Page _dataExplorerContent;
public Page DataExplorerContent
{
get { return _dataExplorerContent; }
set
{
if(value != null)
{
contentFrame.Navigate(value)
SetField(ref _dataExplorerContent, value);
}
}
}
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
say I have this control:
public partial class bloc999 : UserControl
{
bloc999Data mainBlock = new bloc999Data();
public bloc999()
{
InitializeComponent();
mainBlock.txtContents = "100";
base.DataContext = mainBlock;
}
}
in the xaml:
<TextBox Margin="74,116,106,0" Name="txtContents"
Text="{Binding Path=txtContents, UpdateSourceTrigger=PropertyChanged,Mode = TwoWay}" />
<TextBox Margin="74,145,106,132" Name="txtContents2"
Text="{Binding Path=txtContents2, UpdateSourceTrigger=PropertyChanged,Mode = TwoWay}" />
Then I have this class:
public class bloc999Data : INotifyPropertyChanged
{
string _txtContents;
string _txtContents2;
public event PropertyChangedEventHandler PropertyChanged;
void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
public string txtContents2
{
get
{
return this._txtContents2;
}
set
{
if (int.Parse(value) > int.Parse(this._txtContents))
{
this._txtContents2 = "000";
}
else
this._txtContents2 = value;
NotifyPropertyChanged("txtContents2");
}
}
public string txtContents
{
get
{
return this._txtContents;
}
set
{
this._txtContents = value;
NotifyPropertyChanged("txtContents");
}
}
}
Ok now say I have A button on the form and I do this in the code:
mainBlock.txtContents2 = "7777777";
It puts 000 in the textbox, but If i just type in manually, in the textbox (txtContents2), the setter code is called but for some reason the textboxes value does not change, the instance value does change. help?
I believe it's just because the value is changing within the context of the data binding operation, so WPF just ignores it because it knows the value is changing and thinks the event is superfluous. What it doesn't know is that you've gone and changed the value from the value WPF has to something else again.
If you do the notification in a separate message then WPF will process it outside the context of the current data binding operation and will thus pick up the change:
if (int.Parse(value) > int.Parse(this._txtContents))
{
this._txtContents2 = "000";
// notify WPF of our change to the property in a separate message
Dispatcher.BeginInvoke((ThreadStart)delegate
{
NotifyPropertyChanged("txtContents2");
});
}
else
{
this._txtContents2 = value;
NotifyPropertyChanged("txtContents2");
}
This assumes your view model has access to the Dispatcher. An example of how to do so is shown in my blog post on a base ViewModel class.
I was having similar problem earlier here
In your usercontrol, update Binding and set UpdateSourceTrigger to Explicit
<TextBox Margin="74,145,106,132" x:Name="txtContents2" TextChanged="txtContents2_TextChanged"
Text="{Binding Path=txtContents2, UpdateSourceTrigger=Explicit,Mode = TwoWay}" />
then in the TextChanged event handler update the binding manually by validating the input.
move validation logic from property txtContent2's setter in bloc999Data in this event handler
private void txtContents2_TextChanged(object sender, TextChangedEventArgs e)
{
if (int.Parse(txtContents2.Text) > int.Parse(mainBlock.txtContents))
{
mainBlock.txtContents2 = "000";
txtContents2.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
}
else
{
mainBlock.txtContents2 = txtContents2.Text;
txtContents2.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
}
and it works.
Hope it helps!!