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.
CodePudding user response:
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.
CodePudding user response:
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"/>