WPF UserControl Binding - wpf

I've been trying to find a solution to this problem, but I'm quite confused by the possible approaches I've found on the Internet.
I have created a UserControl containing a slider for DateTime.
<UserControl x:Name="root">
<Grid x:Name="gridPanel">
<Slider x:Name="slider" HorizontalAlignment="Left" VerticalAlignment="Top" Height="34" Width="479"
Minimum="{Binding ElementName=root, Path=Minimum, Converter={StaticResource ResourceKey=dateToDoubleConverter}}"
Maximum="{Binding ElementName=root, Path=Maximum, Converter={StaticResource ResourceKey=dateToDoubleConverter}}"
Value="{Binding ElementName=root, Path=Value, Converter={StaticResource ResourceKey=dateToDoubleConverter}}" />
<Label x:Name="lblStartTime" Content="{Binding ElementName=slider, Path=Minimum, Converter={StaticResource ResourceKey=doubleToStringConverter}}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10,73,0,0" FontSize="10"/>
<Label x:Name="lblStopTime" Content="{Binding ElementName=slider, Path=Maximum, Converter={StaticResource ResourceKey=doubleToStringConverter}}" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,73,10,0" FontSize="10"/>
</Grid>
</UserControl>
Code behind:
public SliderPanel() {
InitializeComponent();
gridPanel.DataContext = this;
}
#region Dependency Property - Minimum
public DateTime Minimum {
get { return (DateTime)GetValue(MinimumProperty); }
set { SetValue(MinimumProperty, value); }
}
public static readonly DependencyProperty MinimumProperty =
DependencyProperty.Register("Minimum", typeof(DateTime), typeof(SliderPanel), new UIPropertyMetadata(DateTime.Now));
#endregion
#region Dependency Property - Maximum
public DateTime Maximum {
get { return (DateTime)GetValue(MaximumProperty); }
set { SetValue(MaximumProperty, value); }
}
public static readonly DependencyProperty MaximumProperty =
DependencyProperty.Register("Maximum", typeof(DateTime), typeof(SliderPanel), new UIPropertyMetadata(DateTime.Now.AddDays(1)));
#endregion
#region Dependency Property - Value
public DateTime Value {
get { return (DateTime)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(DateTime), typeof(SliderPanel), new UIPropertyMetadata(DateTime.Now, new PropertyChangedCallback(OnValueChanged)));
public static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
SliderPanel sP = (SliderPanel)d;
if (e.Property == SliderPanel.ValueProperty) {
sP.slider.Value = ((DateTime)e.NewValue).Ticks;
}
}
#endregion
In my main window, I use the UserControl and bind some properties via code, such as:
System.Windows.Data.Binding bndPlayTime = new System.Windows.Data.Binding("CurrentPlayTime");
bndPlayTime.Source = controller;
bndPlayTime.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
lblCurPlayTime.SetBinding(System.Windows.Controls.TextBox.TextProperty, bndPlayString);
sliderPanel.SetBinding(SliderPanel.ValueProperty, bndPlayTime);
The Controller class implements INotifyPropertyChanged:
public DateTime CurrentPlayTime {
get {
return currentPlayTime;
}
set {
if (DateTime.Compare(currentPlayTime, value) != 0) {
currentPlayTime = value;
NotifyPropertyChanged("CurrentPlayTime");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName) {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
The CurrentPlayTime property is constantly updated by a timer. Now I would like the slider to move accordingly. The binding to the label works. The label is updated on a regular basis. However, the binding to the dependency property of the UserControl does not cause the slider value to update (even though I have implemented a callback method). Am I missing something?
Please bear with me, I'm very new to WPF. I'd really appreciate your help.

Did you try to set the binding mode to TwoWay on your bindings ?

Related

Why doesn't my dependency property change propagate to my usercontrol

I'm building a Windows Phone 8 app. I have a UserControl whose contents should get updated asynchronously. My model implements INotifyPropertyChanged. When I update a value in my model it propagates to a TextBox control as it should but not to the contents of my UserControl.
What part of the puzzle am I missing or is it just not possible?
Here's my reproduction scenario.
App page:
<phone:PhoneApplicationPage x:Class="BindingTest.MainPage">
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button Click="Button_Click" Content="Click" HorizontalAlignment="Left" Margin="147,32,0,0" VerticalAlignment="Top"/>
<TextBlock HorizontalAlignment="Left" Margin="69,219,0,0" TextWrapping="Wrap" Text="{Binding Bar}" VerticalAlignment="Top" Height="69" Width="270"/>
<app:MyControl x:Name="Snafu" HorizontalAlignment="Left" Margin="69,319,0,0" Title="{Binding Bar}" VerticalAlignment="Top" Width="289"/>
</Grid>
</phone:PhoneApplicationPage>
This is the code behind with the model class (Foo)
public partial class MainPage : PhoneApplicationPage
{
Foo foo;
// Constructor
public MainPage()
{
InitializeComponent();
foo = new Foo();
ContentPanel.DataContext = foo;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
foo.Bar = "Gnorf";
}
}
public class Foo : INotifyPropertyChanged
{
string bar;
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string name)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
}
public Foo()
{
Bar = "Welcome";
}
public string Bar
{
get
{
return bar;
}
set
{
bar = value;
OnPropertyChanged("Bar");
}
}
}
The UserControl xaml
<UserControl x:Class="BindingTest.MyControl">
<TextBox x:Name="LayoutRoot" Background="#FF9090C0"/>
</UserControl>
And the code behind for the UserControl
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(MyControl), new PropertyMetadata("", OnTitleChanged));
static void OnTitleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyControl c = (MyControl)d;
c.Title = e.NewValue as String;
}
public string Title
{
get
{
return (string)GetValue(TitleProperty);
}
set
{
SetValue(TitleProperty, value);
LayoutRoot.Text = value;
}
}
}
When I run the example, the UserControl TextBox will contain welcome. When I click on the button the regular TextBox updates to Gnorf but the UserControl still displays Welcome.
I also discovered that if I only bind to the UserControl the PropertyChanged event handler is null when the call to set_DataContext returns. The DataBinding infrastructure seems to infer that the binding to my UserControl is a one-time binding instead of a regular one-way binding.
Any ideas?
Try This:-
<app:UserControl1 x:Name="Snafu" Title="{Binding Bar,Mode=TwoWay}" />
I checked It..This will work..:)

WPF Binding to DependencyProperty of UserControl in DataTemplate is not working

i have a custom userControl with DPs and have the problem that the binding to these properties only works if i use the controls outside of datatemplates.
Outside of Datatemplates works the usercontrol great.
XAML to Test the UserControl in a DataTemplate
<GroupBox Header="DataTemplate" Padding="5">
<ItemsControl ItemsSource="{Binding Dummies}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:Dummy">
<StackPanel Orientation="Horizontal" Margin="2">
<common:QuarterPicker SelectedFirstDay="{Binding Gueltigkeit}" Margin="5,0" />
<!--control the value of the item-->
<TextBlock Text="Gueltigkeit: "/>
<TextBlock Text="{Binding Gueltigkeit}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</GroupBox>
MainWindow codebehind and ViewModel
public partial class QuarterPickerTest : UserControl
{
public QuarterPickerTest()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
public class ViewModel
{
public ViewModel()
{
this.Dummy = new Dummy { Gueltigkeit = DateTime.Today };
this.Dummies = new List<Dummy>
{
new Dummy {Gueltigkeit = DateTime.Today},
new Dummy {Gueltigkeit = DateTime.Today.AddMonths(6)},
new Dummy {Gueltigkeit = DateTime.Today.AddDays(-6)},
};
}
public Dummy Dummy { get; set; }
public List<Dummy> Dummies { get; set; }
}
Here is the code behind of my UserControl
#region SelectedFirstDay
public DateTime SelectedFirstDay
{
get { return (DateTime)GetValue(SelectedFirstDayProperty); }
set { SetValue(SelectedFirstDayProperty, value); }
}
public static readonly DependencyProperty SelectedFirstDayProperty
= DependencyProperty.Register("SelectedFirstDay", typeof (DateTime), typeof (QuarterPicker),
new FrameworkPropertyMetadata(DateTime.Today, SelectedFirstDayPropertyChangedCallback) { BindsTwoWayByDefault = true });
private static void SelectedFirstDayPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var quarterPicker = dependencyObject as QuarterPicker;
var date = (DateTime)args.NewValue;
var quarter = GetQuarter(date);
quarterPicker.UpdateProperties(date.Year, quarter);
if (date.Year == quarterPicker.LeftInfiniteYear && quarter == quarterPicker.LeftInfiniteQuarter)
quarterPicker.ShowPopupButtonLeftInfinite();
}
#endregion
Thanks for any suggestions!

Silverlight how to bind my usercontrol in Data Grid column template

today I getting crazy while trying to do, what I think, is simple thing.
I want to be able to create my usercontrol, and use it in my column template in my datagrid
I have searched and tried several combinations, and nothing appear to work
Can anyone help me?
public class User
{
public string Name { get; set; }
public bool IsValid { get; set; }
}
partial class MyControl : UserControl
{
private string _value;
public string Value
{
get { return _value; }
set { _value = value;
txt.Text = value;
}
}
public MyControl()
{
InitializeComponent();
}
}
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="txt" Text="[undefined]"></TextBlock>
</Grid>
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
var items = new List<User>();
items.Add(new User{Name = "user 1", IsValid = true});
items.Add(new User { Name = "user 2", IsValid = false });
myGrid.ItemsSource = items;
}
}
<Grid x:Name="LayoutRoot" Background="White">
<sdk:DataGrid x:Name="myGrid" AutoGenerateColumns="False" IsReadOnly="True">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn Header="Name">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<SilverlightApplication1:MyControl Value="{Binding Name}"></SilverlightApplication1:MyControl>
<!--<TextBlock Text="{Binding Name}"></TextBlock>-->
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Grid>
Edited:
I also tried the following, but I get no results on my grid:
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="txt" Text="{Binding Value}"></TextBlock>
</Grid>
public partial class MyControl : UserControl
{
public DependencyProperty ValueProperty =
DependencyProperty.Register("Value",
typeof(string),
typeof(MyControl),
new PropertyMetadata(OnValueChanged));
public string Value
{
get
{
return (string)GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
NotifyPropertyChanged("Value");
}
}
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyControl) d).Value = (String)e.NewValue; //ERROR: here I got always empty string
}
public MyControl()
{
InitializeComponent();
}
}
The reason why your first code didn't work is simple. To be able to bind the "Value" property on your "MyControl" (Value={Binding Name}), it has to be a Dependency Property. which you fixed in your second bit of code.
Here's what I did (and that worked well):
<UserControl x:Class="BusinessApplication8_SOF_Sandbox.Controls.MyControl"
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"
d:DesignHeight="300" d:DesignWidth="400" Name="myControl">
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Name="textBlock" Text="{Binding Value, ElementName=myControl}"/>
</Grid>
</UserControl>
For the rest, I used your code.
Another possibility, which should be OK in case you only want the data to flow in one direction ("One Way" from source to target), as it is the case when using the TextBlock control is to update the Text property in the "OnValueChanged". here's the code for the Value property:
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(MyControl),
new PropertyMetadata("", OnValueChanged));
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = (MyControl)d;
var oldValue = (string)e.OldValue;
var newValue = target.Value;
target.OnValueChanged(oldValue, newValue);
}
protected virtual void OnValueChanged(string oldValue, string newValue)
{
textBlock.Text = newValue;
}
and you can remove the binding in xaml:
<TextBlock Name="textBlock" />
this worked for me as well.
Hope this helps ;)
you need to implement the INotifyPropertyChanged interface in your User class so that bound user controls are aware if any of the bound properties change. See the following page with the details how to implement it : http://www.silverlightshow.net/tips/How-to-implement-INotifyPropertyChanged-interface.aspx
As you can see you need to implement the interface and in the setters raise the event OnPropertyChanged
Then it should work with your bindings.
Best,
Tim

WPF - Can't display ObservableCollection in a window

I have an ObservableCollection that I can't seem to get to display in a window. Here is the code:
The View Model:
public class QueryParamsVM : DependencyObject
{
public string Query { get; set; }
public QueryParamsVM()
{
Parameters = new ObservableCollection<StringPair>();
}
public ObservableCollection<StringPair> Parameters
{
get { return (ObservableCollection<StringPair>)GetValue(ParametersProperty); }
set { SetValue(ParametersProperty, value); }
}
// Using a DependencyProperty as the backing store for Parameters. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ParametersProperty =
DependencyProperty.Register("Parameters", typeof(ObservableCollection<StringPair>), typeof(QueryParamsVM), new UIPropertyMetadata(null));
}
public class StringPair: INotifyPropertyChanged
{
public string Key { get; set; }
public string Value
{
get { return _value; }
set { _value = value; OnPropertyChanged("IsVisible"); }
}
private string _value;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The Window xaml:
<Window x:Class="WIAssistant.QueryParams"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Query Parameters" Height="390" Width="504">
<Grid>
<ListBox DataContext="{Binding Parameters}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Key}" ></TextBlock>
<TextBlock Text="{Binding Value}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
The calling code:
// Get Project Parameters
QueryParams queryParams = new QueryParams();
queryParams.ViewModel.Parameters.Add(new StringPair {Key = "#project", Value = ""});
queryParams.ShowDialog();
I have tried putting debug statements in the bindings. The Parameter gets bound to, but the Value binding never gets called.
Any Ideas on how to get my list to show up?
Try
<ListBox ItemsSource="{Binding Parameters}">
or
<ListBox DataContext="{Binding Parameters}" ItemsSource="{Binding}">
Something odd here:
public string Value
{
get { return _value; }
set { _value = value; OnPropertyChanged("IsVisible"); }
}
You're raising a property change event on a different property. Should probably be:
public string Value
{
get { return _value; }
set { _value = value; OnPropertyChanged("Value"); }
}

WPF Binding to local variable

Can you bind to a local variable like this?
SystemDataBase.cs
namespace WebWalker
{
public partial class SystemDataBase : Window
{
private string text = "testing";
...
SystemDataBase.xaml
<TextBox
Name="stbSQLConnectionString"
Text="{SystemDataBase.text}">
</TextBox>
??
Text is set to the local variable "text"
The pattern is:
public string Text {get;set;}
and the binding is
{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}
If you want the binding to update automatically you should make it a DependencyProperty.
I think 3.5 added ElementName to bindings, so the following is a little easier:
<Window x:Name="Derp" ...
<TextBlock Text="{Binding Text, ElementName=Derp}"/>
To bind to a local "variable" the variable should be:
A property, not a field.
Public.
Either a notifying property (suitable for model classes) or a dependency property (sutable for view classes)
Notifying property example:
public MyClass : INotifyPropertyChanged
{
private void PropertyType myField;
public PropertyType MyProperty
{
get
{
return this.myField;
}
set
{
if (value != this.myField)
{
this.myField = value;
NotifyPropertyChanged("MyProperty");
}
}
}
protected void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Dependency property example:
public MyClass : DependencyObject
{
public PropertyType MyProperty
{
get
{
return (PropertyType)GetValue("MyProperty");
}
set
{
SetValue("MyProperty", value);
}
}
// Look up DependencyProperty in MSDN for details
public static DependencyProperty MyPropertyProperty = DependencyProperty.Register( ... );
}
If you're doing a lot of this, you could consider binding the DataContext of the whole window to your class. This will be inherited by default, but can still be overridden as usual
<Window DataContext="{Binding RelativeSource={RelativeSource Self}}">
Then for an individual components you can use
Text="{Binding Text}"
To bind a local variable which is present in your Window class it has to be :
1. Public property
2. A notifying property. For this your window class should implement INotifyPropertyChanged interface for this property.
Then in the constructor
public Assgn5()
{
InitializeComponent();
this.DataContext = this; // or **stbSQLConnectionString**.DataContext = this;
}
<TextBox
Name="stbSQLConnectionString"
Text="{Binding text}">
</TextBox>
Need to add the following line in the int constructor:
this.DataContext = this;
And use this in the XAML:
<TextBox Text = "{Binding SomeProperty}" />
Exmaple:
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public string PersonName { get; set; }
public MainWindow()
{
InitializeComponent();
PersonName = "default";
this.DataContext = this;
}
void button1_Click(object sender, RoutedEventArgs args)
{
MessageBox.Show($"Hello {PersonName}");
}
}
MainWindow.xaml
<StackPanel>
<TextBox Name="textbox1"
Text="{Binding PersonName, Mode=TwoWay}"
/>
<Button Name="button1" Click="button1_Click" Content="Click Me" />
</StackPanel>

Resources