I have a problem to bind a subclass to my XML textbox, I followed this post to do it, but it does not work without using a static class. Is there a way to do without using a static class?
I followed this post as reference.
Binding textbox values to a model in wpf
My code is:
public class Model:INotifyPropertyChanged{
public event PropertyChangedEventHandler PropertyChanged;
private string title;
public string Title{
get {
return title;
}
set {
if (tilte!= value) {
tilte= value;
NotifyPropertyChanged();
}
}
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") {
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ViewModel{
public Model modelObj;
public ViewModel(){
modelObj= new Model();
this.DataContext = modelObj;
modelObj.Title = "New title"; // <--- this don't update xml
}
}
<Page
x:Class="AppTest.Demo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:m ="using:Models"
xmlns:vm ="using:ViewModels"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext>
<m:Model></m:Model>
</Page.DataContext>
<Grid>
<TextBlock Text="{Binding Path=Title}"/>
</Grid>
</Page>
You could set your view model as the data context and bind to Model.Title.
Update
This works:
<Page x:Class="WpfApplication8.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:m ="using:Models"
xmlns:vm="clr-namespace:WpfApplication8.ViewModels">
<Page.DataContext>
<vm:ViewModel/>
</Page.DataContext>
<Grid>
<TextBlock Text="{Binding ModelObj.Title, TargetNullValue='null', FallbackValue='fallback'}"/>
</Grid>
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Model : BindableBase
{
private string title;
public string Title
{
get
{
return title;
}
set
{
if (title != value)
{
title = value;
NotifyPropertyChanged();
}
}
}
}
public class ViewModel : BindableBase
{
private Model modelObj;
public Model ModelObj
{
get
{
return modelObj;
}
set
{
modelObj = value;
NotifyPropertyChanged();
}
}
public ViewModel()
{
ModelObj = new Model();
ModelObj.Title = "New title";
}
}
You should set the Title property of the instance of the Model class that you set as the DataContext of the Page:
<Page.DataContext>
<m:Model Title="New title"></m:Model>
</Page.DataContext>
Or:
<Page.DataContext>
<m:ViewModel />
</Page.DataContext>
<Grid>
<TextBlock Text="{Binding Path=modelObj.Title}"/>
</Grid>
Also, you don't set the DataContext property of a view model. You set the DataContext property of a view to an instance of a view model.
Edit:
modelObj must be a public property (and not a field) in order for you to be able to bind to it:
public Model modelObj { get; set; }
Related
I'm new to C# and UWP. After many days of reading official microsoft docks and stackoverflow I still cannot understand what am I doing wrong.
Here is extremely simple test app.
I am trying to change grid's background on click. When application starts the grid's background is bg-1.jpg as expected. After click the value of property PicturePath have changed, but Grid's background have not.
What am I missing?
Here is a simple xaml page
<Page
x:Class="Test.Views.StartPage"
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">
<Grid PointerPressed="Grid_PointerPressed">
<Grid.Background>
<ImageBrush ImageSource="{Binding PicturePath, Mode=OneWay}" />
</Grid.Background>
</Grid>
</Page>
Code behind
public sealed partial class StartPage : Page
{
public StartPage()
{
this.InitializeComponent();
DataContext = new {
TestBackgroundClass.getInstance().PicturePath,
};
}
private void Grid_PointerPressed(object sender, PointerRoutedEventArgs e)
{
TestBackgroundClass.getInstance().PicturePath = "ms-appx:///Assets/bg-2.jpg";
}
}
and a singleton class
public class TestBackgroundClass : INotifyPropertyChanged
{
private static TestBackgroundClass instance;
private string _picturePath { get; set; }
public string PicturePath
{
get
{
return _picturePath;
}
set
{
_picturePath = value;
NotifyPropertyChanged();
}
}
public TestBackgroundClass()
{
_picturePath = "ms-appx:///Assets/bg-1.jpg"
}
public static TestBackgroundClass getInstance()
{
if (instance == null) instance = new TestBackgroundClass();
return instance;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I'm learning WPF MVVM and I get stuck with one problem. I'm calculating variable value in one class every 200ms, then I'm sending it to ViewModel where PropertyChanged event is fired, but the value did not appear on View. When I copy this code for calculation of variable value to ViewModel constructor then everything is working. Could you give me some hints what am I doing wrong?
Class where I calculate values:
public class CalculatingSecondsTask
{
public string seconds { get; set; }
public CalculatingSecondsTask()
{
Task.Run(async () =>
{
int i = 0;
while (true)
{
await Task.Delay(200);
seconds = (i++).ToString();
UpdateSecondsInViewModel(seconds);
}
});
}
public void UpdateSecondsInViewModel(string secs)
{
ViewModel test = new ViewModel();
test.Seconds_To_Start = secs;
}
My ViewModel:
public class ViewModel : INotifyPropertyChanged
{
private string mSeconds_To_Start;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (propertyName == null)
throw new ArgumentNullException("propertyExpression");
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public string Seconds_To_Start
{
get
{
return mSeconds_To_Start;
}
set
{
mSeconds_To_Start = value;
OnPropertyChanged(nameof(Seconds_To_Start));
}
}
And my View is like:
<Window x:Class="Program.Views.StartWindow"
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:Program.Views"
xmlns:ViewModel="clr-namespace:Program.ViewModels"
mc:Ignorable="d"
Title="Program" Height="110" Width="400">
<Window.DataContext>
<ViewModel:ViewModel/>
</Window.DataContext>
<Grid>
<!-- Text -->
<TextBlock x:Name="Seconds_To_Start" HorizontalAlignment="Center">
<Run Text="{Binding Seconds_To_Start}" />
<Run Text="seconds" />
</TextBlock>
</Grid>
</Window>
Admittedly I am new to wpf. But i have spent some time Googling about it all and I am stumped.
in essence i want to update my TextBlock in my UI using Binding whenever my Model values change.
So this is my Model:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfApplication1
{
public class MyModel : INotifyPropertyChanged
{
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
public string MyField { get; set ; }
}
}
This is my UI:
<Window x:Class="WpfApplication1.MainWindow"
xmlns:viewModels="clr-namespace:WpfApplication1"
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:WpfApplication1"
d:DataContext="{d:DesignInstance viewModels:MyModel, IsDesignTimeCreatable=True}"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<viewModels:MyModel></viewModels:MyModel>
</Window.DataContext>
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding MyModel.MyField}"></TextBlock>
<Button Content="Click Me!" Click="Button_Click" />
</StackPanel>
</Grid>
</Window>
This is my code behind:
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MyModel myModel = new MyModel();
private void Button_Click(object sender, RoutedEventArgs e)
{
myModel.MyField = "has worked";
}
}
}
When i press the button the text does not change on the UI..?
The instance you create in the code behind is not the same as you assign in xaml.
Change the button click event to
private void Button_Click(object sender, RoutedEventArgs e)
{
var model = this.DataContext as MyModel;
model.MyField = "has worked";
}
And the binding in xaml to
<TextBlock Text="{Binding MyField}"></TextBlock>
And in your viewmodel you are not calling the notify property changed. So create a private field and modify the property as below.
private string myField;
public string MyField
{
get { return this.myField; }
set { this.SetProperty(ref this.myField, value); }
}
I'm trying to Bind the Text property of the TextBlock to StaticClass.Percent. Since I couldn't do that (Or could I?) I have defined the LoadingPercent in my ViewModel so that I can bind to it. It is still not working. How can I solve this? Or can I bind to the StaticClass directly and ignore the ViewModel approach?
<Window x:Class="TestBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:testBinding="clr-namespace:TestBinding"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<testBinding:ViewModel/>
</Window.DataContext>
<StackPanel>
<TextBlock Width="100"
HorizontalAlignment="Center"
Text="{Binding LoadingPercent}"/>
<Button Content="Change"
Width="200"
Height="30"
Margin="0 20 0 0"
HorizontalAlignment="Center"
Click="ChangeText"/>
</StackPanel>
</Window>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private void ChangeText(object sender, RoutedEventArgs e)
{
StaticClass.Percentage = 10;
}
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private double loadingPercent;
public double LoadingPercent
{
get { return StaticClass.Percentage; }
set
{
loadingPercent = value;
RaisePropertyChanged("LoadingPercent");
}
}
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public static class StaticClass
{
public static int Percentage { get; set; }
}
Here is a mistake:
private double loadingPercent;
public double LoadingPercent
{
get { return StaticClass.Percentage; }
set
{
loadingPercent = value;
RaisePropertyChanged("LoadingPercent");
}
}
You return in get the StaticClass.Percentage but you assign loadingPercent in set.
I am not sure why you need the static class after all, but if you want to ditch the viewmodel and bind directly to the static property, see here
I want to do something like this diagram Data Binding Diagram.
If i update TextBox text Then update TextBlock text and Property and if i change Property Value then also update Textbox and textBlock text. Please tell me how can i do it using WPF ????
Thank`s For Help.
Im not sure if I understand right your question. Are the two Textboxes in the same view or in different?
Here a solution with 2 textboxes in the same view:
View (xaml):
<Window x:Class="Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Name="mainWindow">
<StackPanel>
<TextBox Name="UpperTextBox" Text="{Binding ElementName=LowerTextBox, Path=Text,UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Name="LowerTextBox" Text="{Binding MyValue, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
View-Codebehind (xaml.cs):
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
}
}
ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _myValue;
public string MyValue
{
get { return _myValue; }
set
{
_myValue = value;
OnPropertyChanged("MyValue");
}
}
}