UWP Classic Binding not updating UI after setting bound value from Inputcontrol - c#

To simplify my problem, in my app I want to change the user's input to all uppercase. So "foo" should be displayed as "FOO" when the TextBox loses focus.
My Xaml:
<Page x:Class="App12.MainPage"
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:local="using:App12"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.DataContext>
<local:MainViewModel />
</Page.DataContext>
<StackPanel Margin="10,50,10,10" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Text="{Binding Name1, Mode=TwoWay}" />
<TextBox Text="{x:Bind Path=vm.Name2, Mode=TwoWay}" />
<Button HorizontalAlignment="Center">Just a control for the TextBox to lose focus</Button>
</StackPanel>
</Page>
My ViewModel
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace App12
{
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
}
private string _name1 = "something";
public string Name1
{
get
{
return _name1;
}
set
{
_name1 = (string)value.ToUpper();
OnPropertyChanged();
}
}
private string _name2 = "something";
public string Name2
{
get
{
return _name2;
}
set
{
_name2 = (string)value.ToUpper();
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged( [CallerMemberName] string propertyName = null )
{
var handler = PropertyChanged;
handler?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
}
And my code-behind
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace App12
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
MainViewModel vm;
public MainPage()
{
this.InitializeComponent();
DataContextChanged += MainPage_DataContextChanged;
}
private void MainPage_DataContextChanged( FrameworkElement sender, DataContextChangedEventArgs args )
{
vm = (MainViewModel)DataContext;
}
}
}
When I use classical binding in a UWP app (First TextBox), this code doesn't work
I see the setter being called, OnNotifyPropertyChanged gets called as well, and the handler is not null. Variable _text gets assigned its new value just fine (all uppercase), but then I never see the getter of public variable Text called.
I've also tried a converter (with ConvertBack implemented), with the same result.
Using x:Bind however (Second TextBox), it does work.
In WPF this also works as expected.
Am I missing something or has Binding changed? According to what Microsoft tells us and what I've seen it shouldn't have.

I found another Q/A in Stackoverflow which says:
The problem here is that the binding system in UWP is "intelligent". For TwoWay bindings, changes to the target will automatically propagate to the source and in this scenario, binding system assumes that the PropertyChanged event will fire for corresponding property in source and it ignores these events. So even you have RaisePropertyChanged or NotifyPropertyChanged in you source, the TextBox still won't update.
BTW I can't figure out how to create a workaround for this problem with the classic TwoWay Binding.

Related

Saving and accessing txt file in WPF application

I am new to creating apps. I am trying to build a WPF app that requires me to have a combobox with hundreds of items. I have all of these items saved in a txt/excel file. Obviously, I don't want to hardcode all the options in XAML/C#.
I have no idea how to go about this. I tried to store the list as a resource but don't know how to access the resource once I've put it in there.
I am looking for the easiest approach that can accomplish what I need.
Thanks!
To get the values in your txt/csvfile is trivial (What's the fastest way to read a text file line-by-line?). To get these values to update your ComboBox's contents is actually fairly involved, but the process is at the heart of WPF and MVVM.
The basic idea is to bind an ObservableCollection<string> object to the ItemSource property in your ComboBox and fill it with the items in your text file. I read the file in the constructor of my view model (more on that below) and put all the lines in the Collection (which is bound to the box) on startup. You can also do this elsewhere dynamically if need be.
The usual way to wire it up to the GUI is to use a DataContext in your MainWindow.xaml file. Typically this is done using the Model View ViewModel (MVVM) pattern. The ViewModel is responsible for communicating between the business logic (Model) and the GUI (View). In WPF this is done by having the ViewModel handle event changes in the GUI and also notify the GUI when the data from the model changes. Here I do this with a class called Notifier that implements INotifyPropertyChanged. Then, ViewModel inherits this class and can talk to the GUI via bindings in the xaml. There are other ways of doing the View notifying in WPF/MVVM, but I find this is the simplest. Every WPF project I work on has this class in it.
You will also need bindings for the SelectedItem and SelectedIndex properties in your ComboBox. By binding these to properties that notify in your view model (via calling Update in the setter) you can control all the behavior you need.
Note that I had to remove the StartupUri="MainWindow.xaml" line in the App.xaml file because I instantiate the MainWindow object in the code behind.
Here is how I did it using the standard Visual Stduio WPF application template. Just to clarify how this conforms to MVVM: the 'View' is the GUI itself (defined in MainWindow.xaml), the 'ViewModel' is the ViewModel class, and the 'Model is trivial. It is just a static set of items from the text file to shove into the combobox.
App.xaml:
<Application x:Class="ComboBoxDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ComboBoxDemo">
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs:
using System.Windows;
namespace ComboBoxDemo
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
ViewModel viewModel = new();
MainWindow mainWindow = new() { DataContext = viewModel };
mainWindow.Show();
}
}
}
MainWindow.xaml:
<Window x:Class="ComboBoxDemo.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:ComboBoxDemo"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:ViewModel}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ComboBox Grid.Column="0"
ItemsSource="{Binding Items, Mode=OneWay}"
SelectedItem="{Binding SelectedItem, Mode=OneWay}"
SelectedIndex="{Binding ItemIndex}"
Margin="5"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace ComboBoxDemo
{
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();
}
}
ViewModel.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
namespace ComboBoxDemo
{
public abstract class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged = null;
protected void Update<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
OnPropertyChanged(propertyName);
}
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChangedEventHandler? handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ViewModel : Notifier
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
// event handling for gui/business logic
// private fields for bound variables
private string _selectedItem = "";
private int _itemIndex = 0;
// properties for binding to GUI
public ObservableCollection<string> Items { get; } = new();
public string SelectedItem
{
get => _selectedItem;
set => Update(ref _selectedItem, value);
}
public int ItemIndex
{
get => _itemIndex;
set
{
Update(ref _itemIndex, value);
// here you can use the index to affect business logic as well
}
}
public ViewModel()
{
Items = new();
string textFile = "items.txt";
using StreamReader file = new StreamReader(textFile);
string? line;
while ((line = file.ReadLine()) is not null)
Items.Add(line);
}
}
}
and my combobox items come from 'items.txt' which I put in the same directory as the .exe:
item1
item2
item3
Screenshot of output:

WPF DataGrid not updating - is it one-way binding?

C#, WPF. I am using a Datagrid with binding. My understanding is that with INotifyPropertyChanged implemented, object properties should update in the Datagrid if they are changed.
Currently this is not happening, although I I have implemented INotifyPropertyChanged and I know from testing that the PropertyChanged event is firing. My guess is that binding is not two-way(?) If that is the case I'm not sure how to set it to two-way. The binding is set in XAML, and the ItemsSource is set later in code-behind:
<DataGrid Name="dataGridxyz" ItemsSource="{Binding}">
dataGridxyz.ItemsSource = foo;
Adding two-way binding in XAML using this syntax causes an error:
<DataGrid Name="dataGridxyz" ItemsSource="{Binding, Mode=TwoWay}">
So I was looking for something like this:
dataGridxyz.ItemsSource = foo;
dataGridxyz.Binding.Mode = TwoWay;
It may be that I could set it to two-way binding either in XAML or code-behind... but I can't see how to do either.
EDIT:
The following is minimal functional example to show the problem. It is a much-simplified version of the real thing which is part of a much bigger project.
When the button is clicked, the Name property is changed but it does not update in the PropertyGrid.
<Window x:Class="testBinding.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"
mc:Ignorable="d"
Title="MainWindow">
<Grid>
<StackPanel Orientation="Vertical">
<DataGrid Name="dg" ItemsSource="{Binding}" AutoGenerateColumns="True"/>
<Button Name="btn" Width="100" Height="20" Content="Test" Click="btn_Click"/>
</StackPanel>
</Grid>
namespace testBinding
{
public partial class MainWindow : Window
{
BindingList<foo> bar = new BindingList<foo>() { new foo() };
public MainWindow()
{
InitializeComponent();
dg.ItemsSource = bar;
}
private void btn_Click(object sender, RoutedEventArgs e)
{
bar[0].Name = "Paul";
}
}
class foo : genericClass, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
class genericClass : INotifyPropertyChanged
{
private string _name = "John";
public string EyeColor = "Blue";
public bool Child = false;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Name
{
get { return _name; }
set
{
_name = value;
MessageBox.Show("Name changed!"); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
OnPropertyChanged("Name");
}
}
}
}
I figured out what was happening here through a combination of guesswork and trial and error. Thanks to those who commented.
It was not caused by one-way binding as I had originally surmised.
This problem was caused by the fact that the foo object in the example above inherits from another class (genericClass) and both implement INotifyPropertyChanged. It seems clear that the existence of the PropertyChanged event in the foo class prevents the DataGrid from updating. I had not expected this behavior since I know that the PropertyChanged event in the inherited class does fire and does update the Name property.
If I remove the PropertyChanged event from foo, then the name updates in the PropertyGrid as expected.
class foo : genericClass, INotifyPropertyChanged
{
//public event PropertyChangedEventHandler PropertyChanged;
}
It leaves me with the problem of how to handle property changes at more than one level of inheritance (i.e. both in a class and in one it inherits from, which seems a valid thing to do) ... but that is perhaps a different question.

WPF databinding not working despite the target datacontext bound to the correct source memory address

The scenario is very simple here. I'm trying to bind a textbox to a property of a class at runtime:
tb.displayValue.DataContext = p.GetValue(currentNode, null);
xaml for the textbox:
<TextBox Name="displayValue" Grid.Column="1"
Style="{StaticResource propertyTextBoxStyle}"
Text="{Binding Path=DataContext,
RelativeSource={RelativeSource Self},
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}">
</TextBox>
When the application starts, the textbox does get populated with the correct information so the source to target binding is working fine. However, when I try and edit a value in the textbox and then switch focus, the changes are not reflected in the actual data structure. The value would stay on the UI, but as soon as I try to reload the UI from the data structure again it defaults back to the original value.
I suspect the binding is not working correctly at first, but after checking the memory address of tb.displayValue.DataContext and comparing it to the actual memory address of the data structure it's an identical match.
INotifyPropertyChanged has been implemented and I have added the OnPropertyChanged call to every setter. After spending two days trying to debug this issue I think I'm really running out of options here so any suggestion would be appreciated.
The simplest two-way binding works this way: you set the DataContext on your Window to a new instance of your MainWindowViewModel class which implements INotifyPropertyChanged, and you set the binding path on your TextBox to the name of the public property on your ViewModel you want to bind to.
I'm trying to show how you need a public property with a get and set to bind to, and how to properly set the DataContext for your window so that all of the controls within it are able to bind to the public properties available on it.
I've never heard of setting the DataContext of a TextBox directly to the return value from a method before, and it just seems wrong, so maybe you are not going about it the right way, and hopefully this helps you see how it can work.
MainWindow.cs
<Window x:Class="DemoWPFApp1.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:vm="clr-namespace:DemoWPFApp1.ViewModels"
Height="300" Width="460" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<vm:MainWindowViewModel />
</Window.DataContext>
<TextBox Name="displayValue" Text="{Binding Path=BoundProperty,
Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
</TextBox>
</Window>
MainWindowViewModel.cs
namespace DemoWPFApp1.ViewModels
{
public class MainWindowViewModel : BaseViewModel
{
private string m_boundProperty;
public string BoundProperty
{
get
{
return m_boundProperty;
}
set
{
m_boundProperty = value; OnPropertyChanged();
}
}
public MainWindowViewModel()
{
BoundProperty = "Some value.";
}
}
}
BaseViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace DemoWPFApp1.ViewModels
{
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propName = null)
{
var e = PropertyChanged;
if (e != null && propName != null)
{
e.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
}
}

Toggle Button Two Way Binding Not Working (Universal Windows Platform)

I am trying to bind the "IsChecked" property on the ToggleButton to "ModelView.IsEnabled".
"ModelView.IsEnabled" is always "false"
but somehow the ToggleButton can still show as "Checked".
Is there anything wrong with the binding?
XAML
...
<Page.Resources>
<ModelView:ModelView x:Key="ModelView"/>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleButton IsChecked="{Binding Source={StaticResource ModelView}, Path=IsEnabled, Mode=TwoWay}">
<TextBlock >UWP Toggle Button</TextBlock>
</ToggleButton>
</Grid>
...
ModelView.cs
using...
namespace App2
{
class ModelView : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler CanExecuteChanged;
private bool _isEnabled;
public bool IsEnabled
{
get {
return _isEnabled;
}
set
{
_isEnabled = false;
OnPropertyChanged("IsEnabled");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
Try this, it worked to me:
1. Xaml code changes:
<Grid>
<Grid.DataContext>
<soHelpProject:MainViewModel/>
</Grid.DataContext>
<ToggleButton IsChecked="{Binding IsToggled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<TextBlock >UWP Toggle Button</TextBlock>
</ToggleButton>
</Grid>
regards,
In your class ModelView, change IsEnabled from this:
public bool IsEnabled
{
get {
return _isEnabled;
}
set
{
_isEnabled = false;
OnPropertyChanged("IsEnabled");
}
}
to this:
public bool IsEnabled
{
get {
return _isEnabled;
}
set
{
_isEnabled = value;
OnPropertyChanged("IsEnabled");
}
}
EDIT: If i use _isEnabled = !value; as you suggested, it still works, with button and state now showing opposite values:
EDIT 2: Now, if you want to properly test your binding, then you could add an extra regular button and do this:
private void button1_Click(object sender, RoutedEventArgs e)
{
myModelView.IsEnabled = !myModelView.IsEnabled;
}
so you can watch your ToggleButton switch between true and false every time you click Test Button. Please note that Test Button is not bound to anything, it's just for testing purposes. See corresponding XAML at the bottom.
The problem is that the way you're doing it, "forcing" IsEnabled to be always false, you're actually sabotaging your own code...:O)
And finally, it is not clear from your code when/where you're assigning your DataContext. Please see below how to do it.
XAML:
<Page.DataContext>
<local:MyModelView/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ToggleButton x:Name="toggleButton1" Content="ToggleButton" IsChecked="{Binding IsEnabled, Mode=TwoWay}" HorizontalAlignment="Center"/>
<TextBlock x:Name="textBlock1" Text="{Binding IsEnabled}" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="126,0,201,286" />
<Button x:Name="button1" Click="button1_Click" Margin="127,400,0,220" Content="Test Button" Height="35" />
</Grid>
Code-behind:
private void Page_Loaded(object sender, RoutedEventArgs e)
{
myModelView = new MyModelView();
this.DataContext = myModelView;
}
I've run into the same problem, be it not with a ToggleButton, but with a TextBox, where I wanted to format the text the user had entered.
In your case you want to change the IsChecked property in your viewmodel and have it reflected in the User Interface straight away (so always be unchecked). The reason you want that is of absolutely no importance.
The problem is that with UWP the getter of your property gets called as you would expect when you click the ToggleButton. The normal action for the ToggleButton is to change from unchecked to checked (and vice versa) and that is what happens in your case. But then you expect that NotifyPropetyChanged signals the control in the UI. And that's where it goes wrong. The getter never gets called when the setter is executed (including NotifyPropertyChanged), so the UI doesn't reflect what you did in your setter.
This is very different from what the TwoWay Binding used to do (and still does in WPF). So there is nothing wrong with your code, but it seems that the binding mechanism has changed, although Microsoft claims it didn't. If you would use x:Bind, it works fine, so hat might solve your problem.
To clarify things more I have taken your example and modified it slightly, to show the problem.
I've put a ToggleButton on the page with a TwoWay binding to a viewmodel, exactly as you did. Clicking on the ToggleButton will switch its state from checked to unchecked and vice versa, even though the setter in my viewmodel Always sets the property to false (so unchecked).
But I've also added a normal button, that I've bound to a command that also modifies the property that the ToggleButton is bound to. Clicking this button calls the setter on the property the ToggleButton is bound to. Of course the setter gets called just the same, but after that the binding to the ToggleButton gets called, so NotifyPropertyChanged in this case does cause a UI update.
If you use the debugger, you can see exactly what i mean.
So your problem can be solved by using x:Bind, or by figuring out another way to update the UI, which you shouldn't have to do if Binding was still working as it used to. Maybe Microsoft has implemented some kind of optimization that now destroys classic Binding.
No special things, just a MainPage and a viewmodel.
My code for MainPage.xaml
<Page x:Class="App10.MainPage"
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:local="using:App10"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:ViewModel x:Key="viewModel" />
</Page.Resources>
<Grid x:Name="mainGrid" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Margin="10,20,10,0">
<Button
x:Name="Button"
Content="UWP Normal button"
Command="{Binding Source={StaticResource viewModel}, Path=SwitchIschecked}"
HorizontalAlignment="Stretch" />
<ToggleButton
x:Name="toggleButton"
Margin="0,10,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
IsChecked="{Binding Source={StaticResource viewModel}, Path=IsChecked,
Mode=TwoWay}">
<TextBlock>UWP Toggle Button</TextBlock>
</ToggleButton>
</StackPanel>
</Grid>
</Page>
The code for MainPage.xaml.cs
using Windows.UI.Xaml.Controls;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace App10
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
}
}
And the code for ViewModel.cs
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace App10
{
public class ViewModel : INotifyPropertyChanged
{
private bool _isChecked;
// property for TwoWay binding with ToggleButton
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
// extra var just to check 'value'
var _value = value;
// now always set it to false
_isChecked = false;
// Try to pass value of _isChecked to user interface
// because there is no check whether the value really
// has changed
// But this only works if the setter is not being called
// directly from the control the property is bound to
OnPropertyChanged();
}
}
private ICommand _switchChecked;
// ICommand for normal button, binding to Command
// calls method to set Property for ToggleButton
public ICommand SwitchIschecked
{
get
{
if ( _switchChecked == null )
_switchChecked = new ChangeChecked( new Action( ChangeVar ));
return _switchChecked;
}
set
{
_switchChecked = value;
}
}
// This will set the property for the ToggleButton
private void ChangeVar()
{
IsChecked = !IsChecked;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged( [CallerMemberName] string propertyName = null )
{
var handler = PropertyChanged;
handler?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
/// <summary>
/// Quick class to implement ICommand
/// </summary>
class ChangeChecked : ICommand
{
Action _execute;
public ChangeChecked( Action execute )
{
_execute = execute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute( object parameter )
{
return true;
}
public void Execute( object parameter )
{
_execute();
}
}
}
IsEnabled property is indicating whether the user can interact with the control. IsPressed is readonly property. So IsChecked is probably what you need.

binding windows 8 textBlock in MVVM light

I 'm having problem with TextBlock/TextBox binding. The TextBlock doesn't display the property's content. When I 'm debugging my app, property has content. How you can do it?
Xaml.
<TextBlock HorizontalAlignment="Left" Margin="730,191,0,0" TextWrapping="Wrap" Text="{Binding XmlContentFile, Mode=TwoWay}" VerticalAlignment="Top" Height="429" Width="465"/>
I was finding simple code in web, but I didn't find code.
Code property
public string XmlContentFile
{
get
{
return this.xmlContentFile;
}
set
{
this.xmlContentFile = value;
}
}
My DataContext
DataContext="{Binding Main, Source={StaticResource Locator}}">
Method load XML file to string variable
public async void XmlContentLoad()
{
if (selectFile != null)
{
try
{
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile storageFile = await storageFolder.GetFileAsync(selectFile);
xmlFileTextContent = await FileIO.ReadTextAsync(storageFile);
}
catch (Exception)
{
throw new Exception("Bug");
}
}
}
The problem is that your XmlContentFile property doesn't raise any notifications when it's changed. Your ViewModel needs to implement INotifyPropertyChanged and raise an event whenever any property has changed.
It's likely that your view and its data bindings are getting setup and executed before XmlContentLoad completes (it's asynchronous). If the binding has already completed before the data is loaded, the only way the binding will happen again is if the property raises a notification that it has changed.
It's also worth pointing out that in your XmlContentLoad method you're setting the private variable and not the public property.
xmlFileTextContent = await FileIO.ReadTextAsync(storageFile);
Setting the private variable will never raise property change notification even if you have the setter code wired up to raise the notification. You'll either need to change XmlContentLoad to set the property and have the OnPropertyChanged notification in the setter (recommended) or you'll need to call OnPropertyChanged after you set the private variable (not recommended).
Hope that helps.
Dev support, design support and more awesome goodness on the way: http://bit.ly/winappsupport
Make sure you are setting the Binding Source correctly :
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded_1" >
<Grid>
<TextBlock Text="{Binding XmlContentFile, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="#FFF3A3A3"/>
</Grid>
and make sure as well you are setting the value of your property in the right place :
public partial class MainWindow : Window,INotifyPropertyChanged
{
private string _xmlContentFile;
public string XmlContentFile
{
get
{
return _xmlContentFile ;
}
set
{
_xmlContentFile = value;
OnPropertyChanged("XmlContentFile");
}
}
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
XmlContentFile = "New Value !";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
True that this answer is not in MVVM, but that won't need much changings except that you will be needing to set your DataContext to your ViewModel.

Categories