I have the code:
<TextBox Width="200" Text="{Binding Value}"></TextBox>
Which works. However the "Value" can be different types. So if I have an bool I want to show a checkbox. I rewrote it as as follow, which kinda works:
<ContentControl Content="{Binding Value}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Path=.}"></CheckBox>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:Double}">
<TextBox Width="200" Text="{Binding Path=.}"></TextBox>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
But now the property isn't updated like before. I have tried setting Mode=Twoway, but it still do not work.
Edit
It worked perfectly fine when I only had the textbox, editing the text of the textbox updated the model. However when I tried doing this with the second code (ContentControl) it just doesn't work.
Code
I'm using Mvvm-light togheter with bindings. The "Value" is bound to an instance of Property
[JsonObject]
public class Property<T> : INotifyPropertyChanged
{
[JsonProperty]
public String name;
public Property(String name, T value)
{
this._value = value;
this.name = name;
}
[JsonIgnore]
public T Value {
get { return _value; }
set {
_value = value;
hot = true;
NotifyPropertyChanged("Value");
}
}
[JsonProperty(PropertyName = "value")]
private T _value;
[JsonIgnore]
public String Name { get { return name; } set { name = value; } }
[JsonProperty]
public bool hot = false;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You should implement INotifyPropertyChanged interface in order to track property changes. I'm sure everything works fine then.
This works for me:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private object value;
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
DataContext = this;
}
public object Value
{
get { return value; }
set
{
this.value = value;
NotifyPropertyChanged("Value");
}
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Value = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Related
I know this has been asked for many times. I read a lot of them and tried different ways but still could not get it to work.
The xaml code is a UserControl:
<Grid Name="middle">
<d:TextBlock Text="{x:Bind LayerNodeData.CleanName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Foreground="WhiteSmoke" FontSize="12" FontFamily="Arial" VerticalAlignment="Center" RelativePanel.RightOf="visibleUI" DoubleTapped="OnEditNameBegin" />
</Grid>
I set both this.DataContext and the Grid's DataContext to the data instance.
c#
public ucLayerRow(ImageLayerNode data)
{
LayerNodeData = data;
DataContext = LayerNodeData;
this.InitializeComponent();
middle.DataContext = LayerNodeData;
LayerNodeData.NotifyPropertyChanged("CleanName"); // test if it work
RefreshUI();
}
Model class
public partial class ImageLayerNode : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
// PropertyChanged is always null.
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public string mCleanName = string.Empty;
public string CleanName {
get => mCleanName;
set { mCleanName = value; NotifyPropertyChanged();}
}
....
}
I tried add a breakpoint to the PropertyChanged and found that it is always null and thus never get called. I also tried changing the mode to OneWay, TwoWays but still nothing.
The textblock is away empty not even getting a value once.
The user control is added like this to the main page. Not sure if it is related.
var rowUI = new ucLayerRow(layerNode);
layerContainer.Children.Add(rowUI);
My UserControl's TextBlock binding doesn't update even once
During the testing, the problem looks that you use design time for usercontrol. <d:TextBlock/> please remove d: and make your usercontrol like the following.
Xaml
<Grid>
<TextBlock
VerticalAlignment="Center"
FontFamily="Arial"
FontSize="12"
Foreground="Red"
Text="{x:Bind LayerNodeData.CleanName, Mode=OneWay}" />
</Grid>
Code behind
public sealed partial class ucLayerRow : UserControl
{
public ucLayerRow(ImageLayerNode data)
{
this.InitializeComponent();
LayerNodeData = data;
}
public ImageLayerNode LayerNodeData { get; set; }
}
public partial class ImageLayerNode : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
// PropertyChanged is always null.
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
private string mCleanName = string.Empty;
public string CleanName
{
get => mCleanName;
set { mCleanName = value; NotifyPropertyChanged(); }
}
}
I have a Listbox where it's items are objects. In these objects I store two colors.
I want to bind these colors with an other object's property, but how can I achieve this?
The listbox looks like this:
Listbox1.Items.Add(new ColorAndMoreClass(Color.Red, Color.Blue));
Far away, in an other class there is a property which I'd like to bind to.
How can I do that?
Your rootclass could look like this.
In the class you have a object representing a different Class.
public class ColorAndMoreClass: INotifyPropertyChanged
{
private Color _c;
private Color _c2;
private OtherClass _example;
public ColorAndMoreClass(Color c, Color c2)
{
_c= c;
_c2 = c2;
}
public OtherClass example
{
get { return _example }
set
{
_example = value;
OnPropertyChanged("example");
}
}
public Color c
{
get { return _c; }
set
{
_c= value;
OnPropertyChanged("c");
}
}
public Color c2
{
get { return _c2; }
set
{
_c2 = value;
OnPropertyChanged("c2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
Your other class could look like this. I just took a simple string.
public class OtherClass : INotifyPropertyChanged
{
private String _someOtherProperty;
public OtherClass(){}
public String someOtherProperty
{
get { return _someOtherProperty; }
set
{
_someOtherProperty= value;
OnPropertyChanged("someOtherProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
In your Code behind make a property the Listbox can bind to
public List<ColorAndMoreClass>> ListOfColorAndMore{ get; set; }
public Window1()
{
ListOfColorAndMore = GetDataThatFillsUpTheProperty();
InitializeComponent();
DataContext = this;
}
Your XAML could then look like this. The Datatemplate is used to tell XAML how to display your object.
<Grid>
<ListBox ItemsSource={Binding ListOfColorAndMore}>
<DataTemplate x:Key="myTaskTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=c.R}" />
<TextBlock Text="{Binding Path=c2.R}"/>
<TextBlock Text="{Binding Path=example.someOtherProperty}"/>
</StackPanel>
</DataTemplate>
</ListBox>
</Grid>
I hope it is this that you mean. But your question is not that clear.
I have the following setup:
XAML:
<ListBox x:Name="MyList" ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Height="20" Width="20" Visibility="{Binding HasInformation, Converter={StaticResource VC}, ConverterParameter=True}" Source="/path/to/information.png" />
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Padding="5,0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Note: The ConverterParameter being passed in simply controls whether the visibility is "Collapsed" (False), or "Hidden" (True), so in this case, I want the visibility to be Hidden.
ViewModel Snippet:
private ObservableCollection<IItem> _MyItems;
public ObservableCollection<IItem> MyItems
{
get
{
return _MyItems;
}
set
{
NotifyPropertyChanged(ref _MyItems, value, "MyItems");
}
}
private IItem _SelectedItem;
public IItem SelectedItem
{
get
{
return _SelectedItem;
}
set
{
NotifyPropertyChanged(ref _SelectedItem, value, "SelectedItem");
}
}
IItem:
public interface IItem
{
string Name { get; }
bool HasInformation { get; set; }
}
I populate an implementation of a list of IItem from a database into the list, and the information icon appears appropriately if HasInformation is true. This all works correctly.
However, if I set HasInformation by hand, the view does not update. I have tried:
In the ViewModel:
OnPropertyChanged("MyItems");
MyItems[MyItems.IndexOf(SelectedItem)].HasInformation = true;
// Note that "SelectedItem" is persisted correctly, and always
// points to the selected item that we want to update.
In the code behind:
MyList.GetBindingExpression(ItemsControl.ItemsSourceProperty).UpdateTarget();
All of these fire the getter of the MyItems property, but the view never updates and the icon never displays. I have ensured that the HasInformation property of the item that I updated does, in fact, remain true. I've attached to the PropertyChanged event to ensure that it's firing a property change for "MyItems" (it is, this also fires the getter), and I've even ensured that it's calling the value converter with the correct value for the HasInformation property (it is!), so what am I missing? Is there something weird with image showing/hiding or visibility value conversion that I'm not handling correctly?
ObservableCollection only notifies the collection changes not the changes in each of the item. In order to achieve your goal one of options is to change the IItem from interface to a class which implements INotifyPropertyChanged interface (or implement it in the IItem concrete type), and hook it with the ViewModel's PropertyChanged delegate (remember to unsubscribe it). See some of my code below.
ViewModel
public class MyViewModel: INotifyPropertyChanged
{
private ObservableCollection<Item> _MyItems;
public ObservableCollection<Item> MyItems
{
get
{
return _MyItems;
}
set
{
if (_MyItems != null)
{
foreach (var item in _MyItems)
{
item.PropertyChanged -= PropertyChanged;
}
}
if (value != null)
{
foreach (var item in value)
{
item.PropertyChanged += PropertyChanged;
}
}
OnPropertyChanged();
}
}
private Item _SelectedItem;
public Item SelectedItem
{
get
{
return _SelectedItem;
}
set
{
_SelectedItem = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Item
public class Item : INotifyPropertyChanged
{
private string _name;
private bool _hasInformation;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public bool HasInformation
{
get { return _hasInformation; }
set
{
_hasInformation = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I'm trying to data bind to a custom data type property FormulaField in WPF. I don't understand if there's something I've missed or if what I'm trying to do can't be done?
I've followed the convention of how I've bound to a primitive and found that hasn't worked, there's not update on the FormulaField property. I've also noticed that the custom data type set method is never hit. I'm using MVVM.
A model:
public class OBQModel : NotificationObject
{
private FormulaField _tovLitres;
public FormulaField TOVLitres
{
get
{
if (_tovLitres.UsesFormula)
{
_tovLitres.Value = ConversionHelper.USBarrelsToLitres(_tovBarrels);
}
return _tovLitres;
}
set
{
_tovLitres = value;
RaisePropertyChanged("TOVLitres");
}
}
}
The NotificationObject implements INotifyPropertyChanged:
public abstract class NotificationObject : DependencyObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged<T>(Expression<Func<T>> action)
{
var propertyName = GetPropertyName(action);
RaisePropertyChanged(propertyName);
}
private static string GetPropertyName<T>(Expression<Func<T>> action)
{
var expression = (MemberExpression)action.Body;
var propertyName = expression.Member.Name;
return propertyName;
}
protected internal void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
FormulaField looks like this:
public class FormulaField
{
public bool UsesFormula { get; set; }
public double Value { get; set; }
}
EDIT
Implementing INotifyPropertyChanged in FormulaField goes stack overflow...
public class FormulaField : INotifyPropertyChanged
{
public bool UsesFormula { get; set; }
public double Value
{
get
{
return Value;
}
set
{
Value = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
The Models sit inside an ObservableCollection in a ViewModel.
An illustration of the View:
<StackPanel>
<DataGrid ItemsSource="{Binding OBQModelCollection}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="new TOV (L)" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox BorderThickness="0"
Text="{Binding TOVLitres.Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
Based upon what you wrote, you are raising INPC on "LiquidGOVLitres", which doesn't seem to appear in your code listing, but you are binding to "TOVLitres".
Fixing this inconsistency will help, but you will also need to implement INPC on the FormulaField if you want changes to its members to be part of your UI.
ETA: After the clarifying edit to your code listing, the remaining task is to implement INPC on your FormulaField class and raise the event accordingly.
Also, if you are using 4.5 you can investigate the new Member Info class which helps avoid the use of magic strings in INPC.
Finally, for semantic clarity, it wouldn't hurt to rename "Value" to "FormulaValue"...
To avoid recursion, try this model...
private double _value;
public double Value
{
[DebuggerStepThrough]
get { return _value; }
[DebuggerStepThrough]
set
{
if (Math.Abs(value - _value) > Double.Epsilon)
{
_value = value;
OnPropertyChanged("Value");
}
}
}
I am currently trying to follow MVVM in C# 4, but having troubles with the bindings working.
Starting from the bottom, here is my Property class that should take care of the property changed for XAML bindings:
namespace Visualizer.MVVM
{
public class Property<T> : DependencyObject, INotifyPropertyChanged
{
//private T _Value;
public T Value
{
get { return (T) GetValue(ValueProperty); }
set
{
SetValue(ValueProperty, value);
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged()
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs("Value"));
}
}
public Property(T val)
{
Value = val;
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(T), typeof(Property<T>));
}
}
My ViewModel for the control looks like this and is instantiated in MainWindow.xaml.cs:
public class CheckboxControlVM
{
public Property<bool> IsChecked { get; set; }
public Property<string> Name { get; set; }
public CheckboxControlVM(bool isChecked, string name)
{
IsChecked = new Property<bool>(isChecked);
Name = new Property<string>(name);
}
}
The control has no code-behind, so here is the XAML for it:
<UserControl x:Class="Visualizer.MVVM.Checkbox"
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:Visualizer.MVVM"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Orientation="Horizontal" x:Name="LayoutRoot">
<CheckBox IsChecked="{Binding Path=IsChecked.Value, Mode=TwoWay}"/>
<TextBlock Text="{Binding Path=Name.Value, Mode=OneWay}"/>
</StackPanel>
</UserControl>
Finally, here is the binding in MainWindow.xaml:
<mvvm:Checkbox DataContext="{Binding Realtime}"/>
I have been stuck on this for a lot longer than I should be and am fairly certain its just a simple issue. Any ideas?
I don't quite get your objective of what you want to achieve with that property design. Normally I don't do that in WPF so I'm not quite sure whether this help or not.
Usually, I do implement INotifyPropertyChanged in ViewModel level, not in the attribute owned by VM. Example:
public class ViewModel : INotifyPropertyChanged{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Second, I do not use DependencyProperty unless I make a WPF user control. So I use private property and trigger the OnPropertyChanged with the property name.
private string _name;
public string Name{
set{
_name = value;
OnPropertyChanged("Name");
}
}
Last, in the XAML, I use binding with UpdateSourceTrigger=PropertyChanged.
<TextBlock Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Maybe you can try to add UpdateSourceTrigger=PropertyChanged in your binding, but I am not sure if it will work.
try this
public class CheckboxControlVM
{
bool _isChecked = false;
string _name ;
public Property<bool> IsChecked { get { return _isChecked} set { _isChecked=value;} }
public Property<string> Name { get { return _name } set { _name =value;} }
public CheckboxControlVM(bool isChecked, string name)
{
_isChecked = isChecked;
_name = name;
IsChecked = new Property<bool>(_isChecked);
Name = new Property<string>(_name);
}
}