I've attached some WPF C# binding code - why doesn't this simple example work? (just trying to understanding binding to a custom object). That is when clicking on the button to increase the counter in the model, the label isn't updated.
<Window x:Class="testapp1.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>
<Button Height="23" HorizontalAlignment="Left" Margin="20,12,0,0"
Name="testButton" VerticalAlignment="Top" Width="126"
Click="testButton_Click" Content="Increase Counter" />
<Label Content="{Binding Path=TestCounter}" Height="37"
HorizontalAlignment="Right" Margin="0,12,122,0"
Name="testLabel2" VerticalAlignment="Top"
BorderThickness="3" MinWidth="200" />
</Grid>
</Window>
namespace testapp1
{
public partial class MainWindow : Window
{
public TestModel _model;
public MainWindow()
{
InitializeComponent();
InitializeComponent();
_model = new TestModel();
_model.TestCounter = 0;
this.DataContext = _model;
}
private void testButton_Click(object sender, RoutedEventArgs e)
{
_model.TestCounter = _model.TestCounter + 1;
Debug.WriteLine("TestCounter = " + _model.TestCounter);
}
}
public class TestModel : DependencyObject
{
public int TestCounter { get; set; }
}
}
thanks
For this simple example, consider using INotifyPropertyChanged and not DependencyProperties!
UPDATE
If you do want to use DPs, use the propdp snippet in VS2010 or Dr WPF's snippets for VS2008?
TestCounter needs to be a DepenencyProperty
public int TestCounter
{
get { return (int)GetValue(TestCounterProperty); }
set { SetValue(TestCounterProperty, value); }
}
// Using a DependencyProperty as the backing store for TestCounter.
//This enables animation, styling, binding, etc...
public static readonly DependencyProperty TestCounterProperty =
DependencyProperty.Register
("TestCounter",
typeof(int),
typeof(TestModel),
new UIPropertyMetadata(0));
You can implement the INotifyPropertyChanged interface in the System.ComponentModel namespace. I usually implement a Changed method that can take a number of property names and check for the event not being set. I do that because sometimes I have multiple properties that depend on one value and I can call one method from all of my property setters.
For instance if you had a Rectangle class with Width and Height properties and an Area read-only property that returns Width * Height, you could put Changed("Width", "Area"); in the property setter for Width.
public class TestModel : INotifyPropertyChanged
{
int m_TestCounter;
public int TestCounter {
get {
return m_TestCounter;
}
set {
m_TestCounter = value;
Changed("TestCounter");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
void Changed(params string[] propertyNames)
{
if (PropertyChanged != null)
{
foreach (string propertyName in propertyNames)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Related
Problem: When attempting to bind an Observable Collection to my user control, it is always showing null at runtime.
Description: I have a user control as described below. The goal is to create a button to cycles through an array of images with every click. However, when I run this, ImageCollection is always null, regardless of how I setup the binding on the implementation side. I'm at a loss for why this is. The code is as follows:
XAML:
<UserControl x:Class="kTrack.ToggleImage"
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:kTrack"
mc:Ignorable="d"
d:DesignHeight="{Binding ImageHeight}" d:DesignWidth="{Binding ImageWidth}">
<Grid x:Name="LayoutRoot">
<Button Click="ToggleImage_Click" Height="{Binding ImageHeight}" Width="{Binding ImageWidth}">
<Image Source="{Binding ActiveImage}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ToolTip="{Binding ToolTip}" />
</Button>
</Grid>
</UserControl>
Code-Behind (Important Bits)
public partial class ToggleImage : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty ImageCollectionProperty = DependencyProperty.Register("ImageCollection", typeof(ObservableCollection<string>), typeof(ToggleImage), new PropertyMetadata(null));
...
public ObservableCollection<String> ImageCollection
{
get => (ObservableCollection<String>)GetValue(ImageCollectionProperty);
set => SetValue(ImageCollectionProperty, value);
}
private ImageSource _activeImage;
public ImageSource ActiveImage
{
get => _activeImage;
set
{
_activeImage = value;
OnPropertyChanged();
}
}
private int _currentImageIndex;
public int CurrentImageIndex
{
get => _currentImageIndex;
set
{
_currentImageIndex = value;
OnPropertyChanged();
}
}
// Constructor
public ToggleImage()
{
ImageCollection = new ObservableCollection<String>();
InitializeComponent();
LayoutRoot.DataContext = this;
CurrentImageIndex = 0;
if (ImageCollection.Count > 0)
{
ActiveImage = SetCurrentImage(CurrentImageIndex);
}
}
...
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML Implementation:
<local:ToggleImage ImageHeight="32" ImageWidth="32"
ImageCollection="{Binding Images, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
Code-Behind of Implementation:
public partial class MainWindow : kWindow
{
public ObservableCollection<String> Images = new ObservableCollection<String>()
{
"pack://application:,,,/Resources/Icons/Close.png",
"pack://application:,,,/Resources/Icons/Minimize.png",
"pack://application:,,,/Resources/Icons/WindowOne.png"
};
public MainWindow()
{
InitializeComponent();
}
}
for one, you should set DataContext in Window.
for two, Binding work with properties, not fields: change Images field to property.
public partial class MainWindow : kWindow
{
public ObservableCollection<String> Images { get; } = new ObservableCollection<String>()
{
"pack://application:,,,/Resources/Icons/Close.png",
"pack://application:,,,/Resources/Icons/Minimize.png",
"pack://application:,,,/Resources/Icons/WindowOne.png"
};
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
So, I have a project with a scrolling text (marqee) that rotates over a string array. And I want it to change the string value after 20 seconds of each animation iteration.
There is a problem though, the property(ScrollingText) that uses the INotifyPropertyChanged interface to bind to a textblock(using XAML) does not return after the first iteration. Even though it refreshes normally(in the set part), it does not return on the Getter part.... except for the first set in the default ctor.
MAIN CLASS:
class GetScrollingText : CommonBase
{
private string _scrollingtext = String.Empty;
DoubleAnimation Animation;
public GetScrollingText()
{
ScrollingText = GetScrollString();
}
public string ScrollingText
{
get
{
return _scrollingtext;
}
set
{
if (value != _scrollingtext)
{
_scrollingtext = value;
RaisePropertyChanged("ScrollingText");
}
}
} // INJECTS the string in the animated textblock {binding}.
public TextBlock scrollBlock { get; set; }
string GetScrollString()
{
.........
return scrolltext;
}
public void LeftToRightMarqee(double from, double to)
{
Animation = new DoubleAnimation();
Animation.From = from;
Animation.To = to;
Animation.Duration = new Duration(TimeSpan.FromSeconds(20));
Animation.Completed += animation_Completed;
scrollBlock.BeginAnimation(Canvas.LeftProperty, Animation);
}
void animation_Completed(object sender, EventArgs e)
{
ScrollingText = GetScrollString();
scrollBlock.BeginAnimation(Canvas.LeftProperty, Animation);
}
}
For some reason the animation_Completed Event only changes the value ScrollingText, but it does not invoke the Getter part therefore there is not a return to the {binding}.
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:AnnouncingSys"
x:Class="AnnouncingSys.MainWindow"
x:Name="Window"
Width="1280" Height="720" MinHeight="566" MinWidth="710">
<Window.Resources>
<vm:GetScrollingText x:Key="ScrollingText"/>
</Window.Resources>
<Canvas x:Name="MainCanvas" ClipToBounds="True" Margin="0,0,0,0" Grid.Row="5" Background="Black" Grid.ColumnSpan="5" >
<TextBlock x:Name="ScrollBlock" TextWrapping="Wrap" VerticalAlignment="Top" Height="113" Width="5147" Canvas.Left="-1922" Text="{Binding ScrollingText, Source={StaticResource ScrollingText}}"/>
</Canvas>
</Window>
CODE BEHIND:
public partial class MainWindow : Window
{
GetScrollingText scrolling = new GetScrollingText();
public MainWindow()
{
InitializeComponent();
scrolling.scrollBlock = this.ScrollBlock;
scrolling.LeftToRightMarqee(2000, -3000);
}
}
And finally the helper class CommonBase:
public class CommonBase : INotifyPropertyChanged
{
protected CommonBase()
{
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string PropertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
PropertyChangedEventArgs e = new PropertyChangedEventArgs(PropertyName);
handler(this, e);
}
}
}
I have even put a breakpoint on the return block of the Getter but it only activates on the first: "ScrollingText = GetScrollString()". I mean, shouldn't it return each time the value is changed???
You are using two different instances of your GetScrollingText class, one in XAML as StaticResource, the other in code behind as the scrolling field in class MainWindow.
Instead of creating a StaticResource in XAML, you could just set the DataContext property of your MainWindow:
public partial class MainWindow : Window
{
GetScrollingText scrolling = new GetScrollingText();
public MainWindow()
{
InitializeComponent();
scrolling.scrollBlock = this.ScrollBlock;
scrolling.LeftToRightMarqee(2000, -3000);
DataContext = scrolling; // here
}
}
Now you would not explicitly set the binding's Source property, because the DataContext is used as default binding source:
<TextBlock ... Text="{Binding ScrollingText}"/>
I am trying WPF to develop a tiny scoreboard.
In this project i have 3 XAML files.
ControlDisplay.xaml : Here is where i set the points for team 1 and team 2 in the scoreboard. Right now i only have 1 textbox for the scoreboard title.
Layout1.xaml : First layout, contains only a title for now.
Layout2.xaml : Second layout, same as above, only contains a title.
My idea is as following. I update one singleton class that has one property Title. Both Layout1 and Layout2's label for the title will bind to this singleton class property Title.
I created the basic structure for it.
ControlDisplay.xaml:
public partial class ControlDisplay : Window
{
private IScoreboardData _scoreboardData;
private Layout1 _layout1;
private Layout2 _layout2;
public ControlDisplay()
{
InitializeComponent();
_scoreboardData = SimpleInjectorContainer.Container.GetInstance<IScoreboardData>();
}
private void ShowLayout1(object sender, RoutedEventArgs e)
{
_scoreboardData.Title = "Test";
_layout1 = new Layout1();
_layout1.Show();
}
private void ShowLayout2(object sender, RoutedEventArgs e)
{
_scoreboardData.Title = "Test";
_layout2 = new Layout2();
_layout2.Show();
}
}
Layout1.xaml.cs (layout2 is a copy of layout1 codewise, just a different class name)
public partial class Layout1 : Window
{
private IScoreboardData _scoreboardData;
public Layout1()
{
_scoreboardData = SimpleInjectorContainer.Container.GetInstance<IScoreboardData>();
InitializeComponent();
}
}
Layout1.xaml
<Window x:Class="SmallScoreboard.Layout1" .... x:Name="LayoutOne">
<StackPanel>
<Label DataContext="{Binding ElementName=LayoutOne}" Content="{Binding _scoreboardData.Title}" />
</StackPanel>
</Window>
ScoreboardData.cs
public ScoreboardData : IScoreboardData
{
public string Title { get; set; }
}
This obviously does not work since i don't register a dependency property anywhere? How can i register a dependency property inside the ScoreboardData class? or is there a better way to solve this?
I want to be able to add more layouts in the future and i hope that i don't have to add the base binding logic to each and everyone of those layout(x).xaml.cs files.
Update
This is my Layout1.xaml file right now:
<Window x:Class="Simple_Scoreboard.Layout1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Layout" Height="500" Width="800"
ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden"
WindowStyle="None"
AllowsTransparency="True"
ResizeMode="CanResizeWithGrip"
x:Name="LayoutOne" MouseLeftButtonDown="DWindow_MouseLeftButtonDown">
<StackPanel>
<Label Content="{Binding Path=Title, Mode=OneTime}" FontSize="30" HorizontalAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Center" VerticalContentAlignment="Center" Margin="0,10,0,0" FontWeight="Bold"></Label>
<Button Content="Button" Click="Button_Click_1"/>
</StackPanel>
</Window>
and the Layout1.xaml.cs
public partial class Layout1 : Window
{
public IScoreboardData _scoreboardData;
public Layout1()
{
InitializeComponent();
_scoreboardData = ScoreboardContainer.Container.GetInstance<IScoreboardData>();
DataContext = _scoreboardData;
}
private void DWindow_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DragMove();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_scoreboardData.Title = "Click change title";
}
}
and finally the ScoreboardData class:
class ScoreboardData : IScoreboardData, INotifyPropertyChanged
{
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Title"));
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
I think your problem is in binding to a private field _scoreboardData;
you should make it a public property. But much better solution would be to bind to a window DataContext.
in the window constructor
public Layout1()
{
_scoreboardData = SimpleInjectorContainer.Container.GetInstance<IScoreboardData>();
InitializeComponent();
DataContext = _scoreboardData;
}
In the XAML
<Window x:Class="SmallScoreboard.Layout1" .... x:Name="LayoutOne">
<StackPanel>
<Label Text="{Binding Title}" />
</StackPanel>
</Window>
This way you have your scoreBoardData as Window DataContext and all bindings without explicitly specified source will bind to that object.
UPDATE:
ScoreboardData should implement INotifyPropertyChanged..
public class ScoreboardData :IScoreboardData, System.ComponentModel.INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _title;
public string Title
{
get { return _title; }
set
{
_title = value;
if(PropertyChanged!=null)
PropertyChanged(this,new PropertyChangedEventArgs("Title"));
}
}
}
I have created blank C#/XAML Windows 8 application. Add simple XAML code:
<Page
x:Class="Blank.MainPage"
IsTabStop="false"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
Margin="0,150"
HorizontalAlignment="Center">
<TextBlock
x:Name="xTitle"
Text="{Binding Title, Mode=TwoWay}"/>
<Button Content="Click me!" Click="OnClick" />
</StackPanel>
</Grid>
</Page>
And the simple code in C# part:
public sealed partial class MainPage
{
private readonly ViewModel m_viewModel;
public MainPage()
{
InitializeComponent();
m_viewModel = new ViewModel
{
Title = "Test1"
};
DataContext = m_viewModel;
}
private void OnClick(object sender, RoutedEventArgs e)
{
m_viewModel.Title = "Test2";
}
}
Now I want to implement ViewModel. I have two way:
Use Dependency Property
Implement INotifyPropertyChanged
For first approach it is:
public class ViewModel : DependencyObject
{
public string Title
{
get
{
return (string)GetValue(TitleProperty);
}
set
{
SetValue(TitleProperty, value);
}
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string)
, typeof(ViewModel)
, new PropertyMetadata(string.Empty));
}
For second it is:
public class ViewModel : INotifyPropertyChanged
{
private string m_title;
public string Title
{
get
{
return m_title;
}
set
{
m_title = value;
OnPropertyChanged("Title");
}
}
protected void OnPropertyChanged(string name)
{
if (null != PropertyChanged)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
I prefer the first way, because it allows use coerce (Silverlight for web and for WP7 doesn't have coerce functionality.. WinRT too.. but I'm still looking and hope) and looks more natural for me. But unfortunately, it works as OneTime for the first approach.
Could anybody explain to me why MS abandon using Dependency Property for implementing view model?
You should not be using a DependencyProperty in your ViewModel - you should only use them in your controls. You will never want to bind one ViewModel to another, also ViewModels do not need to persist their values nor provide default values, nor provide property metadata.
You should only use INotifyPropertyChanged in your ViewModels.
While learning c# and wpf, i am trying to get grasp of data binding. So far unsuccessful. All the help i could find on the net, even if described as "for beginners", is too complicated for me to begin to understand. I would appreciate if someone can provide a code for binding in a very simple example:
namespace BindingTest
{
class TestClass
{
public int testProperty;
public TestClass()
{
testProperty = 10;
}
}
}
namespace BindingTest
{
public partial class Window1 : Window
{
TestClass iTestClass = new TestClass();
public Window1()
{
InitializeComponent();
}
private void buttonAdd10_Click(object sender, RoutedEventArgs e)
{
iTestClass.testProperty += 10;
}
}
}
<Window x:Class="BindingTest1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Height="25"
Margin="52,0,130,89"
Name="textBox1"
VerticalAlignment="Bottom"
/>
<Button Height="34"
HorizontalAlignment="Left"
Margin="38,40,0,0"
Name="buttonAdd10"
VerticalAlignment="Top"
Width="62"
Click="buttonAdd10_Click">
+10</Button>
</Grid>
</Window>
All I want to do is to bind textBox1.Text to iTestClass.testProperty, so that when I click the button I can see its value change in the textbox. What changes should be made in the code of this simple example in order to accomplish that?
If it is possible to be done without INotifyPropertyChanged, that is the way I wanna do it.
Thank you in advance!
Vladimir
To bind to a property in WPF you have two options.
a) INotifyPropertyChanged
b) DependencyProperty
Since you don't want to use INotifyPropertyChanged, you're left with DependencyProperty.
In the code-behind of MainWindow, add this line in the constructor:
this.DataContext = iTestClass;
In the XAML of MainWindow, add this attribute to the TextBox:
Text="{Binding testProperty}"
Change TestClass as follows.
class TestClass : DependencyObject
{
public int testProperty
{
get { return (int)GetValue(TestPropertyProperty); }
set { SetValue(TestPropertyProperty, value); }
}
public static readonly DependencyProperty TestPropertyProperty =
DependencyProperty.Register("testProperty", typeof(int), typeof(TestClass));
public TestClass()
{
testProperty = 10;
}
}
If you'd prefer the INotifyPropertyChanged version, change TestClass to this (the rest is the same):
class TestClass : INotifyPropertyChanged
{
private int _testProperty;
public int testProperty
{
get { return _testProperty; }
set
{
if (_testProperty == value)
return;
_testProperty = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("testProperty"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public TestClass()
{
testProperty = 10;
}
}
Why do you need this? Well WPF has no way of telling if the property has changed unless you tell it somehow.
The two ways WPF can tell this is with an event, which INotifyPropertyChanged provides, or by registering the property within the WPF system, which is how the DependencyProperty version works.
If you don't do one of these then WPF won't be able to tell when the value of the property changed, and so binding won't work.