I have used many ComboBoxes in my applications and all of them are working without any problem. But, I can't find the problem now. I have set SelectedValuePath to "Tag" property. But the property not updating after changing the ComboBox selected item. I have read other StackOverflow questions, but nontheless helped.
It is xaml:
xmlns:vms="clr-namespace:SilverlightApplication1"
<UserControl.DataContext>
<vms:MainViewModel />
</UserControl.DataContext>
<Grid x:Name="LayoutRoot" Background="White">
<ComboBox Width="100" VerticalAlignment="Center" FontFamily="Segoe UI"
Height="30" Margin="0,5,0,0" HorizontalAlignment="Left"
SelectedValue="{Binding SelectedDifStatusComparer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="Tag">
<ComboBox.Items>
<ComboBoxItem Tag="H" >High</ComboBoxItem>
<ComboBoxItem Tag="L" >Low</ComboBoxItem>
<ComboBoxItem Tag="E" >Equal</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
</Grid>
And here is the ViewModel:
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _selectedDifStatusComparer = "";
private string SelectedDifStatusComparer
{
get { return _selectedDifStatusComparer; }
set
{
_selectedDifStatusComparer = value;
MessageBox.Show(_selectedDifStatusComparer);
OnPropertyChanged("SelectedDifStatusComparer");
}
}
public MainViewModel()
{
SelectedDifStatusComparer = "E"; // It is working, the MessageBox is apperaing
}
}
Your property is private. Change it to public and it should work.
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _selectedDifStatusComparer = "";
public string SelectedDifStatusComparer
{
get { return _selectedDifStatusComparer; }
set
{
_selectedDifStatusComparer = value;
MessageBox.Show(_selectedDifStatusComparer);
OnPropertyChanged("SelectedDifStatusComparer");
}
}
public MainViewModel()
{
SelectedDifStatusComparer = "E"; // It is working, the MessageBox is apperaing
}
}
Your property is private. Change it to public and it should work.
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 Textbox in WPF which has its "Text" Property bound to a string "EmployeeSource.ID" with Mode=TwoWay. My problem is that when i change the EmployeeSource object, the binding does not work. What is wrong in my approach?
XAML
<TextBox x:Name="NameTextBox" Margin="5,5,10,5" TextWrapping="Wrap"
Text="{Binding SelectedEmployee.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1" Grid.Column="1" />
Code Behind
private Employee _selectedEmployee;
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
_selectedEmployee = value;
UpdateTextBoxes();
}
}
private void UpdateTextBoxes()
{
NameTextBox.Text = SelectedEmployee?.Name;
}
Please try the code below. You need to implement the INotifyPropertyChanged interface inorder to achieve data binding in WPF. This is the basic concept of WPF data binding and MVVM pattern. This should work for you.
Code behind:
public class YourClassName : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Employee _selectedEmployee;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
// The constructor is private to enforce the factory pattern.
private YourClassName()
{
_selectedEmployee = new Employee();
}
public Employee selectedEmployee
{
get
{
return this._selectedEmployee;
}
set
{
if (value != this._selectedEmployee)
{
this._selectedEmployee = value;
NotifyPropertyChanged("selectedEmployee");
}
}
}
}
XAML :
<TextBox x:Name="NameTextBox" Margin="5,5,10,5" TextWrapping="Wrap"
Text="{Binding selectedEmployee.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1" Grid.Column="1" />
I want to display the string _lunedi in a textbox, the string _lunedi is updated periodically using InitializeBoxes().
I have my class:
public event PropertyChangedEventHandler PropertyChanged;
private string _lunedi= "lunedi ";
public string lunedi
{
get { return this._lunedi; }
set
{
if (value != this._lunedi)
{
this._lunedi = value;
NotifyPropertyChanged("lunedi");
}
}
}
public void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
whit this method change the lunedi:
private void InitializeBoxes()
{
lunedi += todayPivot.Day;
}
xaml:
<Border>
<TextBlock Text="{Binding Path=lunedi, UpdateSourceTrigger=PropertyChanged}"></TextBlock>
</Border>
The problem is that the text of the textblock is empty.
Thank you.
Sounds like the binding is using one time try this instead
<Border>
<TextBlock Text="{Binding Path=lunedi, Mode=OneWay}"></TextBlock>
Im attempting to bind a gridView to a ObservableCollection in a Windows Store App. My problem is I get the data but the set does not seem to work
public class ValveData : INotifyPropertyChanged
{
private string valveName;
private decimal waterMl;
private decimal waterEc;
private decimal waterPh;
public string ValveName
{
get { return this.valveName; }
set
{
this.valveName = value;
this.OnPropertyChanged("ValveName");
}
}
public decimal WaterMl
{
get { return this.waterMl; }
set
{
this.waterMl = value;
this.OnPropertyChanged("WaterMl");
}
}
public decimal WaterEc
{
get { return this.waterEc; }
set
{
this.waterEc = value;
this.OnPropertyChanged("WaterEc");
}
}
public decimal WaterPh
{
get { return this.waterPh; }
set
{
this.waterPh = value;
this.OnPropertyChanged("WaterPh");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ValveDataCollection : ObservableCollection<ValveData>
{
public ValveDataCollection() : base()
{
Add(new ValveData { ValveName = "Valve 1", WaterMl = 100, WaterEc = 3, WaterPh = 5 });
Add(new ValveData { ValveName = "Valve 2" });
Add(new ValveData { ValveName = "Valve 3" });
Add(new ValveData { ValveName = "Valve 4" });
}
}
and this is my Xaml
<data:ValveDataCollection x:Key="ValvesData"/>
<GridView x:Name="gw1" Grid.Row="2" ItemsSource="{StaticResource ValvesData}">
<GridView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding ValveName, Mode=TwoWay}"/>
<TextBox Text="{Binding WaterMl, Mode=TwoWay}"/>
<TextBox Text="{Binding WaterEc, Mode=TwoWay}"/>
<TextBox Text="{Binding WaterPh, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Im sure im missing something but I've been looking at this code for days and can't figure it out
The string looks like it works, meaning if i have 2 textboxes linked to the ValveName String the set the string and get the data but it looks like its not working for the decimals, they get the data on startup but if you modify it in the textbox it doesn't seem to affect the variable
The correct XAML should look like
<TextBox Text="{Binding ValveName, Mode=TwoWay}"/>
rather than
<TextBox Text="{Binding ValveName}, Mode=TwoWay"/>
Edit
On a side note:
This code isn't thread safe. Between this.PropertyChanged != null and this.PropertyChanged another thread could unsubscribe and PropertyChanged becomes null.
public void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Always use a local copy of the event handler!
public void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
This one is thread now safe
For future readers: With C# 6.0 (Visual Studio 2015) you can also use the below version, which is shorter and thread safe, using the new "Null Propagation Operator" operator.
public void OnPropertyChanged(string propertyName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I've got a usercontrol which defines a ContentControl like this:
<ContentControl x:Name="PART_contentHost" Grid.Row="1"/>
In the viewmodel I will get a viewModel which will be displayed inside the contentControl. To establish the link with the view I have a datatemplate that establish the relationship between both of them.
<DataTemplate DataType="{x:Type ViewModels:Test1ViewModel}">
<Views:Test1View />
</DataTemplate>
This means that I want Test1ViewModel to be shown inside the contentControl. I am not able to stablish that in my code C#.
//this gets the contentControl from de template
contentHost = this.Template.FindName(contentHostName, this) as ContentControl;
//this assigns the test1ViewModel
contentHost.Content = content
What am I missing?
You have not shared enough code for me to be sure what you are trying to do. While there are cases in which you will need to parse templates, most often there is a better way. So here is how I understand your case in a MVVM context, can you do it this way?
Xaml:
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type local:Test1ViewModel}">
<local:Test1View />
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding ContentModel}" />
</Grid>
Test1View:
<UserControl x:Class="WpfApplication1.Test1View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBlock Text="{Binding Name}" Background="Beige" Padding="5" />
<TextBlock Text="{Binding Address}" Background="PeachPuff" Padding="5" />
</StackPanel>
</UserControl>
ViewModels:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private Test1ViewModel _contentModel;
public Test1ViewModel ContentModel { get { return _contentModel; } set { _contentModel = value; OnPropertyChanged("ContentModel"); } }
public ViewModel()
{
this.ContentModel = new Test1ViewModel() { Name = "John Higgins", Address = "Wishaw" };
}
}
public class Test1ViewModel : INotifyPropertyChanged
{
private string _name;
public string Name { get { return _name; } set { _name = value; OnPropertyChanged("Name"); } }
private string _address;
public string Address { get { return _address; } set { _address = value; OnPropertyChanged("Address"); } }
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I have done something of that nature before.
This code should get you started.
public void FindAndSetTemplateContent( ContentControl target, ViewModelBase item)
{
if (target == null)
throw new ArgumentNullException("target");
if (item == null)
throw new ArgumentNullException("item");
var template = target.TryFindResource(new DataTemplateKey(item.GetType())) as DataTemplate; // this will pick up your resource for the viewmodel
if (template == null)
return null;
var content = template.LoadContent() as ContentControl ;
if (content != null)
{
content.DataContext = item;
}
return content;
}