C# WPF I cannot get private to update but public will - c#

I am very puzzled about this and have searched but cannot find the answer. I am sure I have not understood something correctly. Can you please explain this to me?
I have a View and a ViewModel. In the view I have a Textblock
<TextBlock
Grid.Row="4"
Grid.Column="5"
Grid.ColumnSpan="3"
IsEnabled="{Binding Enable, Mode=OneWay}"
Margin="5,10,5,10">
<Run Text="File: "/>
<Run Text="{Binding FilePathName}"/>
</TextBlock>
At the top of the View I have
d:DataContext="{d:DesignInstance d:Type=viewModels:MainWindowViewModel}"
I also added in Code behind
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
I did not want to add the DataContext here but it seemed at first to make it work but now I am not sure I need it and I will try removing it.
I think I understand this part that the data in the View will be refreshed from the item called FilePathName in the ViewModel.
Now in the view model I have created the “get and set” for the FilePathName as:
private string _filepathname;
public string FilePathName
{
get => _filepathname;
set
{
if (_filepathname != value)
{
_filepathname = value;
OnPropertyChanged();
}
}
}
Now this is where my understanding must be going a bit hazy. I think that within the ModelView I will just use the private string. This means when I change its value like this:
_filepathname = “MyNewName”;
then the PropertyChanged will see this change and will update the View via the public FilePathName. However if I use the FileOpenDialog and open a file and then say
if (openFileDialog.FileName != "")
{
_filepathname = openFileDialog.FileName;
ProcessFile();
}
I am using the private here but this will never update. If I put a break point in the “get and set” we never go there so the UI never updates.
However in ProcessFile() I use
StreamReader sr = new StreamReader(_filepathname);
I do actually read the correct file. This I could understand as it is also private. BUT … if I now change the private to public as follows
if (openFileDialog.FileName != "")
{
FilePathName = openFileDialog.FileName;
ProcessFile();
}
I now update on the UI and open the correct file for reading. It seems sometimes I need to use private and sometimes public to make it work. Why?
A second question is why does isEnabled not seem to work on TextBlock. I have it working on buttons and Textbox. I use it in TextBlock as follows and you can see it further above.
IsEnabled="{Binding Enable, Mode=OneWay}"
All help appreciated.

This does not work like that.
If it was, why would we need properties?
This:
myString = "new value"
ONLY assignes value to variable. And that's all. When you do this using property, property setter will be called, so:
if(myString != value)
{
myString = value; //new value will be assigned to variable
OnPropertyChanged(); //property changed will be called
}
When you assign value to variable, then you can just read it and that's what happens.
This variable is called "backing field". It's there because there has to be some variable that stores a value. If you declare property like that:
public string MyString {get; set;}
under the hood, the compiler also creates such backing field and in the end it works like that:
string __myString;
public string MyString
{
get {return __myString;}
set {__myString = value;}
}
so, as you can see, this is just a synthatic sugar. If you want to get more out of properties (like call property changed), you have to create such bakcing field by yourself.
What's more, property doesn't store any value by itself. Think of it as it has been a method. Actually two methods - one that assigns value to variable and the other that returns the value:
public void SetMyString(string value)
{
myString = value;
}
public string GetMyString()
{
return myString;
}
Because property at all is also some kind of synthatic sugar.
So to sum up:
string myString;
public string MyString
{
get {return myString;}
set
{
if(myString != value)
{
myString = value;
OnPropertyChanged();
}
}
}
///
myString = "NewValue"; //<-- only assigns value to variable myString
MyString = "NewValue"; //<-- assings value to variable myString AND calls OnPropertyChanged - because that's how you property setter looks like.

Your view and view model are two different classes.
When you change a private field of your view model, your view has no way to know if it is not notified. You notify the view when you call OnPropertyChanged() in the property setter. The property setter is not called when you modifiy the field. Read Adam's anwser for more details.
To answer your second question, IsEnabled is not the property you are looking for, as it disables the UI element, but does not hide it. You need to change the Visibility property for that (with a converter).
Here is how I would do it (with other simplifications):
<!-- in application or control resources -->
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
<TextBlock Grid.Row="4"
Grid.Column="5"
Grid.ColumnSpan="3"
Text="{Binding FilePathName, StringFormat=File: {0}}"
Visibility="{Binding Enable, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}"
Margin="5,10"/>

Related

WPF bindings do not pick up any changes

In my WPF application, I have some properties which I have bound to the XAML counterpart, but for some reason do not get set whenever their values change. I have implemented the INotifyPropertyChanged interface as well as set my DataContext for this View too, and it is still not picking up any changes.
I have this same pattern for other properties within this ViewModel which do work, while others don't.
Here is a snippet of my current code:
ViewModel
public class TestViewModel : INotifyPropertyChanged
{
private string testString;
public TestViewModel()
{
.....
this.RunCommand = new RelayCommand(this.RunAction);
}
public string TestString
{
get
{
return this.testString;
}
set
{
this.testString = value;
this.OnPropertyChanged("TestString");
}
}
private void RunAction()
{
.....
this.testString = "Running.";
}
}
View
<StatusBarItem>
<TextBlock Text="{Binding Path=TestString, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
</StatusBarItem>
DataContext (set in code-behind of another MainWindow class)
var testViewModel = SimpleIoc.Default.GetInstance<TestViewModel>();
var testWindow = new TestWindow() { DataContext = testViewModel };
testingWindow.Show();
If it helps, this is part of a multi-windowed application which uses MVVM-Light to pass properties between classes.
You are not changing the value of the TestString, you are assigning a command to change the value but you do not seem to be executing it.
this.RunCommand = new RelayCommand(this.RunAction);
Bind that command to something or execute it manually from somewhere.
Also you need to assign the property not the field
this.TestString = "Running.";
I found the problem. You are only updating the private property testString. But you do not update the property TestString so the notify is never called.
Try this:
this.TestString = "Running";

WPF: Binding TextBox Text to a sub-element of a Property with WCF?

I have a TextBox which I'm trying to bind to a element of a table property 'regimeAlias' is a column with the tbRegimes table which I have mapped with Entity Framework:
<TextBox Text="{Binding NewRegime.regimeAlias, Mode=TwoWay}"/>
Exposed property in my ViewModel:
private tbRegime _NewRegime;
public tbRegime NewRegime
{
get { return _NewRegime; }
set
{
_NewRegime = value;
OnPropertyChanged("NewRegime");
}
}
Lastly, here's the WCF Service Reference auto-generated code class:
public partial class tbRegime : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
//blah blah blah
[System.Runtime.Serialization.DataMemberAttribute()]
public string regimeAlias {
get {
return this.regimeAliasField;
}
set {
if ((object.ReferenceEquals(this.regimeAliasField, value) != true)) {
this.regimeAliasField = value;
this.RaisePropertyChanged("regimeAlias");
}
}
}
The setter never gets hit. Is this because each element within the NewRegime object needs to raise PropertyChanged and if so is there an easy workaround without adding a further DTO layer to my code?
Edit3: with the post from your regimeAlias code. i have to say your binding should work. but of course if you wanna debug you have to set the breakpoint in your regimeAlias setter
<TextBox Text="{Binding NewRegime.regimeAlias, Mode=TwoWay}"/>
this code means, you bind to a Public Property regimeAlias in your class tbRegime.
your setter for NewRegime will never hit because you dont bind to it.
so check your tbRegime class property setter for regimeAlias.
EDIT: the DataContext of the TextBox is of course an object with the Public Property NewRegime, but like i said if you use dot notation in your binding the last property is the one you bind to :)
EDIT: you dont have much ways to workaround:) if you let the binding like you did, you need a model with a public property regimeAlias and it should implement INotifyPropertyChanged.
if you wanna wrap the regimeAlias Property then you have the problem the you have to raise OnPropertyChanged("MyRegimeAlias") at the right point.
public string MyRegimeAlias
{
get { return _NewRegime.regimeAlias; }
set
{
_NewRegime.regimeAlias = value;
OnPropertyChanged("MyRegimeAlias");
}
}
xaml
<TextBox Text="{Binding MyRegimeAlias, Mode=TwoWay}"/>

Set TextBox Text programmatically and update the model

I'm updating a TextBox.Text value in C# but the model binded to wasn't updated.
When I update that visually it works (commenting this line myTextbox.Text="new value"; and inputing manually the value at runtime)
<TextBox Name="myTextbox" Text="{Binding myValue}"/>
I think you need to implement INotifyPropertyChanged.
In your view model you need to call notify of property changed for binding value.
private string _myValue;
public string MyValue
{
get { return _value; }
set
{
_myValue = value;
NotifyOfPropertyChanged("MyValue");
}
}
writing "new value" in your textbox(ui) its not the same as myTextbox.Text="new value" in your codebehind(c#)
so my firsst question would be why you need myTextbox.Text="new value"??

WPF Data Binding TooWay XAML to C# and C# to XAML

I have XAML code:
<TextBox Name="textBoxMask1"/>
<TextBox Name="textBoxMask2"/>
<TextBox Name="textBoxMask3"/>
...
<TextBox Name="textBoxMask9"/>
and class in C#:
private static string mask1;
public static string Mask1
{
get { return mask1; }
set { mask1 = value; }
}
private static string mask2;
public static string Mask2
{
get { return mask2; }
set { mask2 = value; }
}
private static string mask3;
public static string Mask3
{
get { return mask3; }
set { mask3 = value; }
}
....
private static string mask9;
public static string Mask9
{
get { return mask9; }
set { mask9 = value; }
}
And I want to bind these TextBoxes with Properties -> textBoxMask1 with Mask1 etc.
Earlier I did this by TextChanged, but I want to make Binding. TooWay Binding, because I want to predefine Mask1, Mask2, Mask3, ..., Mask9 in another C# class, and maybe later change these values - also in some C# code - and I want my changes, to be visible in layout (XAML) and in C# code - so ex. changing Property Mask1 from C# will change Text in TextBox textBoxMask1, and changing Text in textBoxMask1 will change Property Mask1.
I don't understand, how to make connection (binding) between objects XAML and C#.
For a normal Binding you don't need your properties to be static, just public. Here an example:
C# code (for one property)
private string mask1;
public string Mask1
{
get { return mask1; }
set
{
mask2 = value;
RaisePropertyChanged("Mask1");
}
}
It's really important for the binding that the class containing the properties implements the INotifyPropertyChanged interface, and that you raise the corresponding event in the setter of each property. Another option is to make all properties DependecyProperty, but it is usually overkill.
As for the XAML:
<TextBox Name="textBoxMask1" Text="{Binding Mask1, Mode=TwoWay}"/>
(TwoWay Binding is the default for the Text property, but it does not hurt to put it explicitly).
Just make sure that the DataContext of the object containing your TexBoxes (usually an UserControl) is set to a valid instance of your C# class.
By the way, this is a very basic question, that's why you got a negative vote and no answers before mine. What is expected is that you ask a question that poses a real problem for you, with a very specific answer, not something like "teach me how to do this".
If this answers your question don't forget to mark it as answer (the "tick" mark on the top left). A vote up would be also appreciated.
Hope it helps, regards.

WPF TextBox MaxLength -- Is there any way to bind this to the Data Validation Max Length on the bound field?

ViewModel:
public class MyViewModel
{
[Required, StringLength(50)]
public String SomeProperty { ... }
}
XAML:
<TextBox Text="{Binding SomeProperty}" MaxLength="50" />
Is there any way to avoid setting the MaxLength of the TextBox to match up my ViewModel (which could change since it is in a different assembly) and have it automatically set the max length based on the StringLength requirement?
I used a Behavior to connect the TextBox to its bound property's validation attribute (if any). The behavior looks like this:
/// <summary>
/// Set the maximum length of a TextBox based on any StringLength attribute of the bound property
/// </summary>
public class RestrictStringInputBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
AssociatedObject.Loaded += (sender, args) => setMaxLength();
base.OnAttached();
}
private void setMaxLength()
{
object context = AssociatedObject.DataContext;
BindingExpression binding = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
if (context != null && binding != null)
{
PropertyInfo prop = context.GetType().GetProperty(binding.ParentBinding.Path.Path);
if (prop != null)
{
var att = prop.GetCustomAttributes(typeof(StringLengthAttribute), true).FirstOrDefault() as StringLengthAttribute;
if (att != null)
{
AssociatedObject.MaxLength = att.MaximumLength;
}
}
}
}
}
You can see, the behavior simply retrieves the data context of the text box, and its binding expression for "Text". Then it uses reflection to get the "StringLength" attribute. Usage is like this:
<UserControl
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<TextBox Text="{Binding SomeProperty}">
<i:Interaction.Behaviors>
<local:RestrictStringInputBehavior />
</i:Interaction.Behaviors>
</TextBox>
</UserControl>
You could also add this functionality by extending TextBox, but I like using behaviors because they are modular.
While I'm not going to write the code out completely myself, one idea is to create your own MarkupExtension that will take the property name and reflect over looking for a StringLengthAttribute.
If the attribute exists, attempt to bind the target to that value (using reflection). If not, then bind 0 to the target value (0 is default, i.e. no max).
One way to do it would be to create a property in that same viewmodel called SomePropertyMaxLength and then bind the MaxLength property to that property.
<TextBox Text="{Binding SomeProperty}" MaxLength="{Binding SomePropertyMaxLength}"/>
The Markup extension is definitely the way to go.
I am creating a subclass of BindingDecoratorBase called Binding which has a model DataType dependency property. As MarkupExtensions are created during InitializeComponent() there is no way to determine the DataContext as it will not have been set yet.
Providing the model type permits reflective access to attributes defined on the model.
This permits:
Setting MaxLength for TextBoxes.
Setting StringFormat for TextBlocks.
Setting the default Converter depending on member data type.
Adding required validation. Using either the binding's ValidationRules or by setting ValidatesOnDataErrors.
The markup looks like:
Text="{PO:Binding DataType=model:modAccount, Path=SubAccount}"
Formatting, MaxLength, and Conversion rolled into one package with no need to change anything as the model classes change.
Or you can have your model only to accept a max # chars:
private string _MyText { get; set; }
public string MyText { get => _MyText; set => _MyText = value?.Substring(0,
Math.Min(value.Length, 15)); }
Text="{Binding Path=MyText}"

Categories