I'm having difficulties with getting a bound textbox to update. I'm still new to WPF development and I must be missing a fundamental concept as I've read nearly everything available on the internet at this point and I'm still confused. Below is my code. First, an overview of what I'm doing to better set the context for my question.
Mainwindow is a Window that contains tabs that load various pages using frame source tags. I believe this might be causing me issues as I'm not sure where the actual object is getting instantiated for each tab, just that the XAML is being loaded.
Scratchpad is a class that contains a textbox, which is going to be updated and used by almost all classes that perform any type of operation to report status and any errors.
Textbox XAML (this is in "ScratchPad_View.xaml" for context)
<TextBox x:Name="scratchMessage"
Text="{Binding Path=ScratchMessage, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Right"
Height="300"
Width ="500"
TextWrapping="Wrap"
VerticalAlignment="Top"/>
Code behind XAML
public partial class ScratchPad : Page
{
public ScratchPad()
{
InitializeComponent();
ScratchPad_Model ScratchPad_Model = new ScratchPad_Model();
this.DataContext = ScratchPad_Model;
}
}
Model Implementation
class ScratchPad_Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string _scratchMessage;
public string ScratchMessage;
{
get
{
return _scratchMessage;
}
set
{
if (value != _scratchMessage)
{
_scratchMessage = value;
OnPropertyChanged("ScratchMessage");
}
}
}
// Create the OnPropertyChanged method to raise the event
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Most of this I have cobbled together via responses to other questions on StackOverflow and reading numerous databinding tutorials however it's still not clicking. I'm not sure how to update the contents of the textbox and since I'm loading the page that contains the textbox in the XAML of my mainwindow I'm not sure I'm even referencing the correct object. The mainwindow loads this page in a frame tag, copied below.
<Frame Source="Common/View/ScratchPad_View.xaml" ></Frame>
In the code behind for this XAML, I have the following.
public partial class MainWindow
{
// Create scratchpad object for logging and status display
ScratchPad scratchPad = new ScratchPad();
public MainWindow()
{
InitializeComponent();
}
private void StartVault(object sender, RoutedEventArgs e)
{
// Creates the authentication prompt view object and pass the scratchPad reference for reporting
authPrompt_View _authPrompt_View = new authPrompt_View(scratchPad);
}
}
I pass the reference to the ScratchPad object that I created in the initialization of the mainwindow to all classes so that they can update the contents of the textbox, however I haven't had much luck in getting the binding to work. Once it works, I'm still not quite sure how I'm supposed to append text to the textbox. There's probably a great deal of problems here but I'm hoping to fix some of my conceptual problems and get a better understanding of what I'm doing wrong, thanks in advance!
You can use Application.Properties to set global properties for your project. So probably in SETTER method of textbox bound variable (in your case ScratchMessage), you need to set property in global application properties collection.
Below links explains it very well:
https://msdn.microsoft.com/en-us/library/aa348545(v=vs.100).aspx
http://www.c-sharpcorner.com/Resources/842/application-properties-in-wpf-and-c-sharp.aspx
My understanding is that , You have created the ViewModel for ScratchPad inside the constructor and assigning the DataContext in the same.
So, other windows will not have access to the DataContext.
My suggestion is that Maintain a base ViewModel class and inherit the base Viewmodel in all other ViewModel's.
Add ScratchMessage property inside base viewModel.
So you can access the ScratchMessage property from other viewModel's too.
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _scratchMessage;
public string ScratchMessage
{
get { return _scratchMessage; }
set
{
_scratchMessage = value;
this.OnPropertyChanged("ScratchMessage");
}
}
// Create the OnPropertyChanged method to raise the event
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ViewModel1 : BaseViewModel
{
ViewModel1()
{
this.ScratchMessage = "Message";
}
}
Related
I am trying to create simple imitation of MVVM using Winforms.
I have alreadt created binding from ViewModel to Form as below:
public class FirstViewModel : ViewModel
{
private string _name;
[Bind(nameof(TextBox.Text), "ExampleTextBox", typeof(ExampleConverter))]
public string Name
{
get => _name;
set
{
_name = value;
RaisePropertyChanged();
}
}
public FirstViewModel()
{
Test();
}
private async void Test()
{
await Task.Delay(TimeSpan.FromSeconds(1));
Name = "Binding working...";
}
}
In above example inside ViewModel class I am calling related Form then find control with given name and set the value.
I am wondering how could I do this as 'generic' as there with return value back to property.
The solution could be to listen TextChanged event for given "ExampleTextBox" but this is not best solution since I would have to know that Text property is realted with OnTextChanged event in this control.
Maybe it is possible to listen for Text property changed and does not matter which one eventhandler will raise that, or maybe I'm going to wrong direction? Did somone faced with that?
Thanks in advance.
The way I've done it in WinForms is by adding the data bindings inside the form itself:
textBox1.DataBindings.Add("Text", _viewModel, "PropertyName");
There are several techniques you can use in order to avoid magic strings.
I would also implement the interface INotifyPropertyChanged in the model:
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I'm quite new to coding in C# and XAML and I just can't get the binding to work in XAML. It works once, when I initialise the presenter class, but doesn't update the Textbox Text, if I change the bound variable in the code afterwards.
When the program starts, "200" is displayed in the Textbox. If I press the Button, all the Messageboxes are displayed (showing "100"), but the Textbox still shows "200" instead of "100".
I tried many solutions I found online, but none seemed worked.
The Presenter Class (ViewModel):
class Presenter : ObservableObject
{
float _xText;
public float xText
{
get { return _xText; }
set
{
_xText = value;
RaisePropertyChangedEvent("xText");
}
}
public ICommand Update
{
get { return new DelegateCommand(_Update); }
}
public Presenter()
{
_xText = 200f;
}
void _Update()
{
MessageBox.Show("_Update");
_xText = 100f;
//Debug
MessageBox.Show(_xText.ToString());
MessageBox.Show(xText.ToString());
}
}
The XAML Code (View):
<TextBox IsReadOnly="False"
IsEnabled="True"
Text="{Binding Path=xText, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<Button Command="{Binding Update}"/>
The ObservableObject class:
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I hope someone has a solution or can explain where I went wrong.
Thanks.
You are updating the backing field not the property so NotifyPropertyChanged is not getting called. Try the property instead
void _Update()
{
MessageBox.Show("_Update");
xText = 100f;
//Debug
MessageBox.Show(_xText.ToString());
MessageBox.Show(xText.ToString());
}
To fix your problem:
You are setting the backing variable _xText so it isn't running the property xText. (As #Ken-Tucker said)
(EXTRA CHANGES)
I never use float in C# or in the (SQL Server) database. (You will regret it.)
See here "the difference between decimal, double and float".
See if you can replace:
RaisePropertyChangedEvent("xText");
with
RaisePropertyChanged(() => xText);
You will avoid a lot of cases of getting stung with typoes.
You have a property named xText? Hope it is just for the question, otherwise not good. You are writing for the reader. Start here with naming guidelines.
I have an application that takes user input for a configuration settings before executing. The UI fields are databound to objects using two-way.
When I make a change in the gui the databound object updates as it should. If I change an object property in CS the GUI updates as it should.
Now the majority of the settings can be templated for different tasks. So I created several templates in JSON format. This JSON template is a dehydrated version of the configuration objects before execution.
So if a person chooses to use a template instead of entering all the settings I take the JSON, rehydrate, then wipe out the existing object with the deserialized json object. This is where I think it is breaking down, because the IPropertyNotify binding doesn't exist, or not properly connected to the json generated object. This code snippet shows the general idea. Full code will be below.
var deserialized = JsonConvert.DeserializeObject<rtfMasterContext>(confFile);
rtf_Master.WorkItemConfig = deserialized.WorkItemConfig;
rtf_Master.WorkspaceItemConfig = deserialized.WorkspaceItemConfig;
rtf_Master.BranchMergeConfig = deserialized.BranchMergeConfig;
Now if I change to a loop which updates the properties and does NOT break the INotify binding then the gui does update with the templated settings.
rtf_Master.WorkspaceItemConfig.SourceBranch = deserialized.WorkspaceItemConfig.SourceBranch;
As promised here is the full code:
public partial class MainWindow : Window
{
rtfMasterContext rtf_Master = new rtfMasterContext();
public MainWindow()
{
InitializeComponent();
this.DataContext = rtf_Master;
}
}
XAML Sample:
<Label>Source Branch</Label>
<TextBox Text="{Binding SourceBranch, Mode=TwoWay}" Margin="5,0,5,0"/>
Context and child properties snippets.
public class rtfMasterContext
{
....snip....
public WorkspaceItemConfig WorkspaceItemConfig { get; set; }
....etc....
}
[DataContract]
public class WorkspaceItemConfig : INotifyPropertyChanged
{
private string _sourceBranch;
[DataMember]
public string SourceBranch
{
get
{
return _sourceBranch;
}
set
{
_sourceBranch = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
So my question is how should I proceed. If I stick with updating just the properties is there a more elegant way then having one line like the above for each property, which would of course be tightly coupled. New configuration value then this would break if not updated.
Perhaps there is a way to replace entire objects as in the first snippet? If so then I'd imagine I would need to mark the IPropertyNotify to be serialized somehow?
I have my XAML code (inside a Page of the standard blank page template) as
<Grid>
<TextBlock x:Name="tbBindingBlock">
</Grid>
And I have an int named iTestBindingin the code-behind. Is there a simple (since I'm only binding one variable and not a collection) way of binding the two together so that the latest value of iTestBinding (which keeps getting changed from the code-behind) is always displayed inside tbBindingBlock ?
Edit : The code behind is pretty short :
public sealed partial class MainPage : Page
{
public int iTestBinding=0;
you can bind your value directly using the below code
<TextBlock x:Name="tbBindingBlock" Text="{Binding iTestBinding}">
keep your value to bind as a property in code behind like this
public int iTestBinding{ get; set; }
and set the datacontext in page loaded event like this
this.DataContext = this;
if you want to update the value on button click and to be reflected in UI, you need to implement the PropertyChangedEventHandler.
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));
}
}
call RaisePropertyChanged("changed property name") where-ever you updated the value of the property.
Its better to keep a custom get set for the property so that we can call this method from the setter.for example
private string myText;
public string MyText {
get {
return myText;
}
set {
myText = value;
RaisePropertyChanged("MyText");
}
}
I am a beginner to use MVVM in WPF and found that it seem impossible to change the value of a textbox or a label. Here is an example.
In Xaml:
The original value of Name is "Peter".
But after I press a button which invoke a command in the ViewModel and change the value of Name to be
"John". So, suppose the value of the text box will be changed to John as well. However, it doesn't change.
I have found a lot of example in the net and found that none of them implemented this kind of functions. What I have learnt from them is to use Command and ItemsSource of ListView.
The value of ListView will change when I use button to raise command to change the ItemsSource of the view. Its value will change automatically when the Binding to ItemsSource changed.
However, I cannot make the value of TextBox or Label change even the value of the bindings to them are changed already.
Actually, I am really quite young in MVVM. I think I still have so much that I don't know.
Could you give me an example of how exactly I should do to make change to textbox after a button click? By the way, I am not quite sure how to make command for button. It seem to involve so much codes that I found in the sample from the net. Is there any simplier way?
Thank you very much.
Your ViewModel needs to implement INotifyPropertyChanged .
Documentation see here
public class Bar : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string foo;
public string Foo
{
get { return this.foo; }
set
{
if(value==this.foo)
return;
this.foo = value;
this.OnPropertyChanged("Foo");
}
}
private void OnPropertyChanged(string propertyName)
{
if(this.PropertyChanged!=null)
this.PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
Your view model should implement INotifyPropertyChanged so that WPF knows that you've altered a value of a property.
Here is an example from
// This is a simple customer class that
// implements the IPropertyChange interface.
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private string customerNameValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
var listeners = PropertyChanged;
if (listeners != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged("CustomerName");
}
}
}
}