I have created a simple .net core wpf application to represent my problem. On the MainWindow there is a TextBox, which should be updated periodically via Binding. But the TextBox only shows 'Hello world' but not the time.
Thanks for any hints!
MainWindow.xaml:
<Window x:Class="WpfTest.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:WpfTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBox Text="{Binding Path=DebugText}" TextWrapping="Wrap" Margin="10,191,10,192"/>
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace WpfTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private readonly ViewModel _viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _viewModel;
}
}
}
ViewModel.cs:
using System;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
namespace WpfTest
{
public class ViewModel : UserControl
{
private Timer _updateTimer;
public ViewModel()
{
DebugText = "Hello world";
AutoResetEvent timerEvent = new AutoResetEvent(true);
_updateTimer = new Timer(OnTimer, timerEvent, 1000, 1000);
}
private void OnTimer(object stateInfo)
{
Application.Current.Dispatcher.Invoke(() =>
{
DebugText = DateTime.Now.ToString();
});
}
public string DebugText
{
get => (string)GetValue(DebugTextProperty);
set => SetValue(DebugTextProperty, value);
}
public static readonly DependencyProperty DebugTextProperty =
DependencyProperty.Register("DebugText",
typeof(string),
typeof(MainWindow),
new PropertyMetadata(null));
}
}
Related
Revised: I apologize for missing some important descriptions in the first version, now the problem should be well-defined:
so I'm making a toy CAD program with following views:
MainWindow.xaml
CustomizedUserControl.xaml
CustomizedUserControl is a Tab within MainWindow, and its DataContext is defined in MainWindow.xaml as:
<Window.Resources>
<DataTemplate DataType="{x:Type local:CustomizedTabClass}">
<local:UserControl1/>
</DataTemplate>
</Window.Resources>
And CustomizedUserControl.xaml provides a canvas and a button, so when the button is pressed the user should be able to draw on the canvas. As the following code shows, the content of Canvas is prepared by the dataContext, "tabs:CustomizedTabClass".
CustomizedUserControl.xaml
<CustomizedUserControl x:Name="Views.CustomizedUserControl11"
...
>
<Button ToolTip="Lines (L)" BorderThickness="2"
Command="{Binding ElementName=CustomizedUserControl11,
Path=DrawingCommands.LinesChainCommand}"
IsEnabled="True"
Content = "{Binding ElementName=CustomizedUserControl11,
Path=DrawingCommands.Button1Name}">
</Button>
...
<canvas x:Name="CADCanvas"
Drawing="{Binding Drawing ,Mode=TwoWay}" >
</canvas>
It is also notable that I used an external library, Fody/PropertyChanged, in all classes so property notifications would be injected without further programming.
CustomizedUserControl.xaml.cs
using PropertyChanged;
using System.ComponentModel;
using System.Windows.Controls;
[AddINotifyPropertyChangedInterface]
public partial class CustomizedUserControl: Usercontrol, INotifyPropertyChanged{
public CADDrawingCommands DrawingCommands { get; set; }
public CustomizedUserControl()
{
InitializeComponent();
DrawingCommands = new CADDrawingCommands(this);
DrawingCommands.Button1Name = "yeahjojo"; //For testing data binding
}
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
}
CADDrawingCommands.cs
using PropertyChanged;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows;
[AddINotifyPropertyChangedInterface]
public class CADDrawingCommands : INotifyPropertyChanged{
UserControl _drawableTab;
public string Button1Name { get; set; } = "TestForDataBinding";
public RoutedCommand LinesChainCommand { get; set; } = new RoutedCommand();
public CADDrawingCommands(UserControl dTab){
_drawableTab = dTab;
CommandBinding lineCommandBinding = new CommandBinding(LinesChainCommand,
(object sender, ExecutedRoutedEventArgs e) =>
{
MessageBox.Show("Test");
//Draw on canvas inside CustomizedUserControl (modify Drawing property in CustomizedTabClass)
}, (object sender, CanExecuteRoutedEventArgs e) => { e.CanExecute = true; });
_drawableTab.CommandBindings.Add(lineCommandBinding);
}
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
}
The Content of Button is set correctly, as I can read the string defined in Button1Name:
Therefore I suppose the Data Binding for Command is also ok. IsEnabled has been set to true and CanExecute of the CommandBinding would only return true.
Why is my button still greyed out and not clickable?
If I define the button inside a Window instead of UserControl (and set the datacontext of the Window to its own code behind, the button will be clickable! Why?
Thank you for your time! Hopefully would somebody help me cuz I've run out of ideas and references.
Made the simplest example.
Everything works as it should.
BaseInpc is my simple INotifyPropertyChanged implementation from here: BaseInpc
using Simplified;
using System.Windows;
using System.Windows.Input;
namespace CustomizedUserControlRoutedCommand
{
public class CADDrawingCommands : BaseInpc
{
UIElement _drawableTab;
private string _button1Name = "TestForDataBinding";
public string Button1Name { get => _button1Name; set => Set(ref _button1Name, value); }
public static RoutedCommand LinesChainCommand { get; } = new RoutedCommand();
public CADDrawingCommands(UIElement dTab)
{
_drawableTab = dTab;
CommandBinding lineCommandBinding = new CommandBinding(LinesChainCommand,
(object sender, ExecutedRoutedEventArgs e) =>
{
MessageBox.Show("Test");
//Draw on canvas inside CustomizedUserControl (modify Drawing property in CustomizedTabClass)
}, (object sender, CanExecuteRoutedEventArgs e) => { e.CanExecute = true; });
_drawableTab.CommandBindings.Add(lineCommandBinding);
}
}
}
<UserControl x:Name="CustomizedUserControl11" x:Class="CustomizedUserControlRoutedCommand.CustomizedUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:CustomizedUserControlRoutedCommand"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Button ToolTip="Lines (L)" BorderThickness="2"
Command="{x:Static local:CADDrawingCommands.LinesChainCommand}"
IsEnabled="True"
Content = "{Binding ElementName=CustomizedUserControl11,
Path=DrawingCommands.Button1Name}">
</Button>
</Grid>
</UserControl>
using System.Windows.Controls;
namespace CustomizedUserControlRoutedCommand
{
public partial class CustomizedUserControl : UserControl
{
public CADDrawingCommands DrawingCommands { get; }
public CustomizedUserControl()
{
DrawingCommands = new CADDrawingCommands(this);
InitializeComponent();
DrawingCommands.Button1Name = "yeahjojo"; //For testing data binding
}
}
}
<Window x:Class="CustomizedUserControlRoutedCommand.TestCustomizedUserControlWindow"
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:CustomizedUserControlRoutedCommand"
mc:Ignorable="d"
Title="TestCustomizedUserControlWindow" Height="450" Width="800">
<Grid>
<local:CustomizedUserControl/>
</Grid>
</Window>
If you showed your code in full, then I see the following problems in it:
You are setting the value incorrectly for the DrawingCommands property.
In this property, you do not raise PropertyChanged.
The binding in the Button is initialized in the InitializeComponent() method. At this point, the property is empty, and when you set a value to it, the binding cannot find out.
There are two ways to fix this:
Raise PropertyChanged in the property;
If you set the property value once in the constructor, then set it immediately in the initializer. Make the property "Read Only". This way, in my opinion, is better.
public CADDrawingCommands DrawingCommands { get; }
public FileEditTabUserControl()
{
DrawingCommands = new CADDrawingCommands(this);
InitializeComponent();
DrawingCommands.Button1Name = "yeahjojo"; //For testing data binding
}
You have a button bound to a command in the DrawingCommands.LinesChainCommand property.
But to this property, you assign an empty instance of the = new RoutedCommand () routing command.
This looks pointless enough.
If you need a routable command, create it in the "Read Only" static property.
This will make it much easier to use in XAML:
public static RoutedCommand LinesChainCommand { get; } = new RoutedCommand();
<Button ToolTip="Lines (L)" BorderThickness="2"
Command="{x:Static local:DrawingCommands.LinesChainCommand}"
IsEnabled="True"
Content = "{Binding ElementName=CustomizedUserControl11,
Path=DrawingCommands.Button1Name}">
</Button>
Raising PropertyChanged in CADDrawingCommands properties is also not visible in your code.
If it really does not exist, then the binding is also unaware of changing property values.
I've dumbed down the code as much as I could to try and get a working piece of code yet I'm still coming up short. Some advice would be appreciated.
I'm trying to get a DependencyProperty working, it's that simple and yet the data I'm setting on the main window isn't showing up in the user control.
In the MainWindow I'm setting the TextValue to "hi" in the xaml. TextValue is showing in the xaml up and compiling just fine so I'm pretty sure I have the DependencyProperty set right. Once the dialog is fully open I take a look in the debugger and my property TextValue is still null.
Am I missing setting the data context? Maybe I'm off base in what I'm looking to do.
Thanks for taking the time to figure out what I'm doing wrong.
My User Control is: UserControl1
Xaml:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Loaded="UserControl_Loaded"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
</Grid>
</UserControl>
UserControl1.xaml.cs is:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("TextValue", typeof(string), typeof(UserControl1));
private string _tv;
public string TextValue
{
get
{
return _tv;
}
set
{
_tv = value;
}
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
}
}
}
My calling window xaml is:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:usercontrols="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525"
Loaded='Window_Loaded'>
<Grid>
<usercontrols:UserControl1 x:Name="usercontroltest1" TextValue="hi"/>
</Grid>
</Window>
My calling window .cs is:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
}
The getter and setter of the "property wrapper" must call the GetValue and SetValue methods of the DependencyObject base class like shown below. Besides that, there is a naming convention that mandates that a dependency property's identifier field is named like the property plus a Property suffix. See Custom Dependency Properties for all the details.
public static readonly DependencyProperty TextValueProperty =
DependencyProperty.Register(
nameof(TextValue), typeof(string), typeof(UserControl1));
public string TextValue
{
get { return (string)GetValue(TextValueProperty); }
set { SetValue(TextValueProperty, value); }
}
In order to access a UserControl's dependency property in its own XAML, you would typically use a RelativeSource Binding like this:
<UserControl x:Class="WpfApplication1.UserControl1" ...>
<Grid>
<TextBlock Text="{Binding TextValue,
RelativeSource={RelativeSource AncstorType=UserControl}}" />
</Grid>
</UserControl>
I'm trying to create a usercontrol that can resize and collapse/expand (with an animation). The resizing stop working when I play the collapse/expand animation.
Complete test application can be found here: App
EDIT: here's the relevant code as requested
MyControl.xaml:
<UserControl x:Class="WpfApp1.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<Grid Background="#FF935E5E">
<Thumb Width="8"
HorizontalAlignment="Right"
Margin="0,0,-4,0"
DragDelta="Thumb_DragDelta"
Cursor="SizeWE"/>
</Grid>
MyControl.xaml.cs:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MyControl.xaml
/// </summary>
public partial class MyControl : UserControl
{
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register("IsOpen", typeof(bool), typeof(MyControl), new PropertyMetadata(true, OnIsOpenChanged));
private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyControl control = d as MyControl;
control.PlayAnimation();
}
public double OpenWidth
{
get { return (double)GetValue(OpenWidthProperty); }
set { SetValue(OpenWidthProperty, value); }
}
public static readonly DependencyProperty OpenWidthProperty =
DependencyProperty.Register("OpenWidth", typeof(double), typeof(MyControl), new PropertyMetadata(300d, OnOpenWidthChanged));
private static void OnOpenWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyControl control = d as MyControl;
if (control.IsOpen)
control.Width = control.OpenWidth;
}
public MyControl()
{
InitializeComponent();
if (IsOpen)
Width = OpenWidth;
}
private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
OpenWidth += e.HorizontalChange;
}
private void PlayAnimation()
{
DoubleAnimation sizeAnimation = new DoubleAnimation(IsOpen ? OpenWidth : 0, TimeSpan.FromMilliseconds(250));
sizeAnimation.EasingFunction = new CircleEase() { EasingMode = EasingMode.EaseInOut };
BeginAnimation(WidthProperty, sizeAnimation);
}
}
}
MainWindow.xaml:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="700">
<DockPanel>
<local:MyControl IsOpen="{Binding ControlIsOpen}"
OpenWidth="{Binding ControlOpenWidth}"/>
<Grid Background="Green">
<Button Width="100"
Height="20"
Content="Test Animation"
Click="Button_Click"/>
</Grid>
</DockPanel>
MainWindow.xaml.cs:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion INotifyPropertyChanged
private bool _ControlIsOpen = true;
public bool ControlIsOpen
{
get => _ControlIsOpen;
set
{
_ControlIsOpen = value;
OnPropertyChanged();
}
}
private double _ControlOpenWidth = 300d;
public double ControlOpenWidth
{
get => _ControlOpenWidth;
set
{
_ControlOpenWidth = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ControlIsOpen = !ControlIsOpen;
}
}
}
Thanks for the help :)
The animation actually never stopsYou should specifiy FillBehavior to Stop. In this case the annimation will stop updating the property after the final value is reached.
private void PlayAnimation()
{
DoubleAnimation sizeAnimation = new DoubleAnimation(IsOpen ? OpenWidth : 0, TimeSpan.FromMilliseconds(250));
sizeAnimation.FillBehavior = FillBehavior.Stop;
sizeAnimation.EasingFunction = new CircleEase() { EasingMode = EasingMode.EaseInOut };
sizeAnimation.Completed += OnAnimationCompleted;
BeginAnimation(WidthProperty, sizeAnimation);
}
private void OnAnimationCompleted(object sender, EventArgs e)
{
Width = IsOpen ? OpenWidth : 0;
}
The default value is HoldEnd. And the storyboard will modify the Width untill it is not explicitly stopped.
https://msdn.microsoft.com/en-us/library/system.windows.media.animation.timeline.fillbehavior(v=vs.110).aspx
Some more info https://learn.microsoft.com/en-us/dotnet/framework/wpf/graphics-multimedia/how-to-set-a-property-after-animating-it-with-a-storyboard
Well thanks to Dmitry idea, i've been able to solve it, by setting the fill behavior to stop and forcing to width to be either 0 or the open width:
private void PlayAnimation()
{
DoubleAnimation sizeAnimation = new DoubleAnimation(IsOpen ? OpenWidth : 0, TimeSpan.FromMilliseconds(250));
sizeAnimation.EasingFunction = new CircleEase() { EasingMode = EasingMode.EaseInOut };
sizeAnimation.FillBehavior = FillBehavior.Stop;
sizeAnimation.Completed += (s, e) => Width = (IsOpen ? OpenWidth : 0);
BeginAnimation(WidthProperty, sizeAnimation);
}
Thanks all :)
I'm trying to get my head around WPF, Unity and MvvMlight (galasoft). So far my little set up works. If I run my application the label is filled with a random name generated by my DataService. (small victory getting all moving parts to work)
But in the design view of Visual Studio the label remains empty. How do i convince VisualStudio to render some 'design time' data in my label?
I'm using: Visual Studio Premium 2013, Unity 4.0.1, MvvmLight 5.2, .net 4.5
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IDataService, DataService>();
container.RegisterType<IMainViewModel, MainViewModel>();
MainWindow mainWindow = container.Resolve<MainWindow>();
mainWindow.Show();
base.OnStartup(e);
}
}
In App.xaml I have not defined the StartUpUri
MainWindow.xaml
<Window x:Class="UnityMvvmTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="500">
<Grid>
<Label x:Name="myLabel" Content="{Binding MyText}"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MainWindow(IMainViewModel theViewModel)
: this()
{
this.DataContext = theViewModel;
}
}
MainViewModel.cs
public class MainViewModel : ViewModelBase, IMainViewModel
{
private readonly IDataService _dataService;
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
if (IsInDesignMode)
{
// Code runs in design time data.
MyText = "Design Data";
}
else
{
// Code runs "for real"
MyText = _dataService.GetName();
}
}
public string MyText { get; set; }
}
I found a method, using hints from https://stackoverflow.com/a/3380895/249845
I created a second (flat) implementation of IMainVieModel in a separate namespace: UnityMvvmTest.ViewModel.Design. This implementation has no logic, it just fills the properties so the designer has some data to display.
This implementation is used in design time, since it is specified as the DesignTime DataContext. (with xmlns:d, xmlns:mc and xmlns:vm). The mc-namespace is needed to hide the d-namespace during runtime, see why.
The result is 5 extra lines in the Xaml, an extra (almost empty) implementation of IMainViewModel. And an extra (empty) constructor in code behind, instead of a constuctor that test for IsInDesignMode. This isn't a big deal, since unity will pick the constructor with the most parameters it can resolve.
MainWindow.xaml
<Window x:Class="UnityMvvmTest.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"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:UnityMvvmTest.ViewModel.Design"
d:DataContext="{d:DesignInstance IsDesignTimeCreatable=True, Type=vm:MainViewModel}"
mc:Ignorable="d"
>
<Grid>
<Label x:Name="myLabel" Content="{Binding MyText}" />
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
// Contructor used in DesignTime
public MainWindow()
{
InitializeComponent();
}
//Constructor used by Unity
public MainWindow(IMainViewModel theViewModel)
: this()
{
this.DataContext = theViewModel;
}
}
MainViewModel.cs (design time implementation)
namespace UnityMvvmTest.ViewModel.Design
{
public class MainViewModel : IMainViewModel
{
public MainViewModel()
{
MyText = "my Design time data";
}
public string MyText { get; set; }
}
}
New to WPF.
I am trying to bind my Model to my UI. So, when the Property is changed during my User actions I want the field to update whereever it occurs on my UI.
This is my Model:
namespace WpfApplication1
{
public class model2
{
private static string myField2;
public static string MyField2
{
get { return myField2; }
set { myField2 = value; }
}
}
}
My Markup:
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:model2 x:Key="mymodel"/>
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Source={StaticResource ResourceKey=mymodel}, Path=MyField2}"></TextBlock>
<Button Content="static test!" Click="Button_Click_1" />
</StackPanel>
</Grid>
</Window>
My code behind:
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
model2.MyField2 = "static!";
}
}
}
The field on the UI does not change?
You need to notify changes to the UI so it can update with new values.
In your case you want to notify static properties of changes so you would need a static event. The problem is the INotifyPropertyChanged interface needs a member event so you won't be able to go that way.
You best shot is to implement the Singleton pattern:
namespace WpfApplication1
{
public class model2 : INotifyPropertyChanged
{
//private ctor so you need to use the Instance prop
private model2() {}
private string myField2;
public string MyField2
{
get { return myField2; }
set {
myField2 = value;
OnPropertyChanged("MyField2");
}
}
private static model2 _instance;
public static model2 Instance {
get {return _instance ?? (_instance = new model2();)}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And then make your property a member property and bind like this:
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:model2 x:Key="mymodel"/>
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Source={x:Static local:model2.Instance}, Path=MyField2}"/>
<Button Content="static test!" Click="Button_Click_1" />
</StackPanel>
</Grid>
</Window>
Code behind:
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
model2.Instance.MyField2 = "static!";
}
}
}
Use the Static extension to bind the TextBlocks Text Property:
<TextBlock Text="{Binding Source={Static MyModel.MyField2}, Mode=TwoWay">
But still the Property must raise the PropertyChanged event. My understanding why you use the static field is to be able to set the value from somewhere else. Have you thougt about using messages instead? Checkout the MVVM Light toolkit and the messenger. This would decouple the two components
I think that static properties are not what you want to use, from comments I can deduce that you are using only to make your program work. Below is the full working code.
App.xaml
Remove the code StartupUri="MainWindow.xaml instead we will instantiate MainWindow in code-behind to provide DataContext.
App.xaml.cs
Here we are assigning object of Model2 as Window.DataContext and then showing the window.
public partial class App : Application
{
public App()
{
Model2 model = new Model2();
MainWindow window = new MainWindow();
window.DataContext = model;
window.Show();
}
}
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
//We get hold of `DataContext` object
var model = this.DataContext as Model2;
model.MyField2 = "Hello World";
}
}
Model:
public class Model2 : INotifyPropertyChanged
{
private string _myField2;
public string MyField2
{
get { return _myField2; }
set
{
_myField2 = value;
OnPropertyChanged("MyField2");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
MainWindow.xaml
<Window x:Class="WpfApplication2.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">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding MyField2}"></TextBlock>
<Button Content="static test!" Click="ButtonBase_OnClick" />
</StackPanel>
</Grid>
</Window>
And I checked, it works !