Consider following XAML:
<Window x:Class="WpfApplication1.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">
<StackPanel>
<TextBlock Text="{Binding Dic[foo]}" />
<Button Content="test" Click="Button_Click" />
</StackPanel>
</Window>
And Backing code:
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public Dictionary<string, string> Dic { get; set; }
public MainWindow()
{
InitializeComponent();
Dic = new Dictionary<string, string>();
Dic.Add("foo", "bar");
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// Doesn't work :(
Dic["foo"] = "YEAH!";
}
}
}
Here TextBlock properly binds to dictionary item "foo". But how to make it to update when its value is changed?
You need to raise a change notification for the indexer using Binding.IndexerName as property name, you might want to encapsulate that in a new class inheriting or managing Dictionary.
have your dictionnary be a dictionnary(of string, DescriptionObject) Where DescriptionObject has a notifying string property, implements PropertyChanged and has a ToString override.
Then you add (foo, fooDescription) to your dictionnary. If you change fooDescription in your ButtonClick handler, the TextBlock will change too.
You need to add indexer to your code like that:
private Dictionary<string, string> Dic { get; set; }
public string this[string key]
{
get { return Dic[key]; }
set
{
if(key != null && Dic[key] != value)
Dic[key] = value;
OnPropertyChanged("Item[" + key + "]");
}
}
Then, in the xaml you make binding to the indexer, and when the item change it will be notify:
<Window x:Class="WpfApplication1.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">
<StackPanel>
<TextBlock Text="{Binding [foo]}" />
<Button Content="test" Click="Button_Click" />
</StackPanel>
</Window>
Related
In a simple trying-to-learn-WPF experiment I'm trying to bind a property ("InternalName") of an instance of MyModel to the contents of TextBlock "MainWindowTextBlock". Clicking the ``ChangeNameButton" changes the InternalName property of mymodel, but that property change never makes it through to the TextBlock. Nothing happens. What am I doing wrong?
XMAL
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:UserControlExperiments"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel Grid.Row ="0">
<Button Width="100" Height="20" Name="ChangeName" Content="Change the Name" Click="ChangeNameButtonClick"/>
<TextBlock Text=""/>
<TextBlock Name="MainWindowTextBox" Width="100" Height="20" Text="{Binding Path = mymodel.InternalName, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</Window>
CODE BEHIND
public partial class MainWindow : Window
{
public MyModel mymodel;
public MainWindow()
{
InitializeComponent();
DataContext = this.DataContext;
mymodel = new MyModel("The old name");
}
private void ChangeNameButtonClick(object sender, RoutedEventArgs e)
{
mymodel.InternalName = "A new name!";
}
}
public class MyModel : INotifyPropertyChanged
{
private string internalname;
public event PropertyChangedEventHandler PropertyChanged;
public MyModel(string nm)
{
InternalName = nm;
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string InternalName
{
get { return internalname; }
set
{
if (internalname != value)
{
internalname = value;
OnPropertyChanged("InternalName");
}
}
}
}
}
The following markup tries to bind to a property named "mymodel" of the current DataContext of the TextBlock, which is inherited from the parent window:
<TextBlock Name="MainWindowTextBox"
Text="{Binding Path = mymodel.InternalName}"/>
So you need to set the DataContext of the window to itself:
DataContext = this;
And you also need to make mymodel a public property since you cannot bind to fields:
public MyModel mymodel { get; }
Then it should work but you probably also want to change the name of the property to comply with the C# naming standards.
You can also remove Mode=TwoWay from the binding. It makes no sense for a TextBlock.
I'm trying to realize a simple example of a UserControl, showing in a TextBox the current DateTime, updated four times each second.
I create a simple user control:
<UserControl x:Class="UC.TestUC"
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:UC"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="100">
<d:UserControl.DataContext>
<local:TestUC_VM/>
</d:UserControl.DataContext>
<Grid Background="Azure">
<TextBox Text="{Binding TestString}"/>
</Grid>
</UserControl>
Where its ViewModel is:
namespace UC
{
public class TestUC_VM : INotifyPropertyChanged
{
private string _testString;
public string TestString
{
get => _testString;
set
{
if (value == _testString) return;
_testString = value;
OnPropertyChanged();
}
}
public TestUC_VM()
{
TestString = "Test string.";
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow XAML:
<Window x:Class="UC.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:UC"
mc:Ignorable="d"
Title="MainWindow" Height="100" Width="200">
<Window.DataContext>
<local:MainWindow_VM/>
</Window.DataContext>
<Window.Resources>
<local:TestUC_VM x:Key="TestUC_VM"/>
</Window.Resources>
<Grid>
<local:TestUC DataContext="{StaticResource TestUC_VM}"/>
</Grid>
</Window>
And its ViewModel:
namespace UC
{
public class MainWindow_VM
{
public TestUC_VM _uc_VM;
public MainWindow_VM()
{
_uc_VM = new TestUC_VM();
Task.Run(() => ChangeString());
}
public async Task ChangeString()
{
while (true)
{
_uc_VM.TestString = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
await Task.Delay(250);
}
}
}
}
Even though I see with debugger that I'm passing through the TestString setter, the MainWindow is not updated.
I'm quite sure I'm missing something trivial in setting DataContext of UC in MainWindow, but I've not been able to find what after several hours of browsing and thinking.
Any help appreciated.
The expression
<local:TestUC DataContext="{StaticResource TestUC_VM}"/>
assigns the value of the TestUC_VM resource to the UserControl's DataContext. This is a different object than the _uc_VM member of the main view model, which you are later updating.
Turn the member into a public property
public TestUC_VM UcVm { get; } = new TestUC_VM();
and write
<local:TestUC DataContext="{Binding UcVm}"/>
Update the view model like this:
UcVm.TestString = ...
I'm trying to assign different increment values to different fields of an object. For example, consider a class has who has int1 and int2, and when I set ShowAdvancedOptions to true for my PropertyGrid, integer up down buttons are put in the textbox with no problems. But I want to be able to edit how much the numbers are incremented individually. Is there a way I can ahcieve this?
Edit:
Here is the code:
public MainWindow()
{
InitializeComponent();
Sample or = new Sample();
pg.SelectedObject = or;
pg.ShowAdvancedOptions = true;
}
MainWindow.xaml:
<Window
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"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid" x:Class="WpfApp1.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<xctk:PropertyGrid x:Name="pg" HorizontalAlignment="Left" Margin="328,70,0,0" VerticalAlignment="Top" Height="275" Width="341"/>
</Window>
and the Sample class:
public class Sample
{
public enum SampleEnum
{
A,B,C,D,E
}
#region private fields
private SampleEnum _SampleEnum;
private int _Value;
#endregion
#region Public Properties
[Category("Sample")]
[DisplayName("Sample Value")]
[DefaultValue(3)]
public int Value { set; get; }
#endregion
}
You could define a custom EditorTemplate per property:
<xctk:PropertyGrid x:Name="pg">
<xctk:PropertyGrid.EditorDefinitions>
<xctk:EditorDefinition>
<xctk:EditorDefinition.PropertiesDefinitions>
<xctk:PropertyDefinition Name="int1" />
</xctk:EditorDefinition.PropertiesDefinitions>
<xctk:EditorDefinition.EditorTemplate>
<DataTemplate>
<xctk:PropertyGridEditorIntegerUpDown Increment="10" Value="{Binding Value}" />
</DataTemplate>
</xctk:EditorDefinition.EditorTemplate>
</xctk:EditorDefinition>
</xctk:PropertyGrid.EditorDefinitions>
</xctk:PropertyGrid>
In the above sample markup, the int1 property is incremented by 10 instead of 1 which is the default value.
XAML, C# novice and am struggling to databind a variable defined in my code behind to a textblock defined in XAML. But I get not result.
Here is my XAML
<Window x:Class="WpfApplication1.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"
Loaded="Window_Loaded_1">
<Grid>
<TextBlock Name="totalRecording">
<Run Text="44 /"/>
<Run Text="{Binding Source=listlength, Path=totalRecording}"/>
</TextBlock>
</Grid>
Here is my code behind
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
var listlength = 100;
}
}
}
For now I have just set the variable to a static number for the purposes of illustrating my problem but this variable will be obtained from a list Count value.
For binding you need to use Property only .you cannot use varibale for binding.
To create property I have created a class here . It is not necessary to create a new class to have property.
public class TextboxText
{
public string textdata { get; set; }
}
And set datacontext to textblock so that I can use this property for binding
InitializeComponent();
totalRecording.DataContext = new TextboxText() { textdata = "100" };
in xaml
<Grid Height="300" Width="400" Background="Red">
<TextBlock Name="totalRecording">
<Run Text="44 /"/>
<Run Text="{Binding textdata}"/>
</TextBlock>
</Grid
If you want to update the Binding, you should use a DependencyProperty.
First you have to create the property and a public string like this:
public static readonly DependencyProperty ListLengthProperty =
DependencyProperty.Register("ListLength", typeof(string), typeof(Window), new PropertyMetadata(null));
public string ListLength
{
get { return (string)GetValue(ListLengthProperty); }
set { SetValue(ListLengthProperty, value); }
}
Here is the XAML file, you need to set a name for the window:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="CurrentWindow"
Title="MainWindow" Height="350" Width="525"
Loaded="Window_Loaded_1">
<Grid>
<TextBlock Name="totalRecording">
<Run Text="44 /"/>
<Run Text="{Binding ListLength, ElementName=CurrentWindow}"/>
</TextBlock>
</Grid>
Now you can always update the Binding by setting the ListLength like this:
ListLength = "100";
Just use TextBlock,
<Grid Name="myGrid" Height="437.274">
<TextBox Text="{Binding Path=listlength}"/>
</Grid>
Declare the variable and Implement InotifyPropertyChanged
partial class Window1 : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _listlength;
public string Listlength
{
get { return _listlength; }
set
{
if (value != _listlength)
{
_listlength = value;
OnPropertyChanged("Listlength");
}
}
}
}
I have this very simple viewModel:
class ViewModel : IDataErrorInfo
{
public Producto myProduct { get; set; }
public string PR { get; set; }
public ViewModel()
{
myProduct = new Producto { ID = 1, Name = "Product 1" };
PR = "Test";
}
public string Error
{
get { throw new NotImplementedException(); }
}
public string this[string columnName]
{
get
{
string sError = "";
return sError;
}
}
}
And this simple view:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<TextBox Height="40" Width="200" Text="{Binding Path=myProduct.Name,ValidatesOnDataErrors=True}"/>
<TextBox Height="40" Width="200" Text="{Binding Path=PR,ValidatesOnDataErrors=True}"/>
</StackPanel>
</Grid>
Can anybody tell me why validation event is fired for property PR but not for myProduct?
I can't manage to validate fields from an exposed object of the viewmodel! Anyone please!!!
{Binding Path=myProduct.Name, …
For that binding to utilize IDataErrorInfo, the type of myProduct has to implement IDataErrorInfo too. Just like you need to implement INotifyPropertyChanged for subobjects, you need to implement the error info interface too for each subobject.