Why Can't Won't My WinForms Textbox bind? - c#

Basically, I want to bind a textbox in my xaml with a variable, DisplayNumFilter, in my c#. I would like to initialize my textbox to 20. I've been looking through several stack overflow posts and have tried many things, which is the constructor is kind of a mess (I tried many things and just sort of left them there). However, nothing's worked. My apologies with any mistakes in regards to formatting or terminology, I am still very new to this.
Here is a snippet of my xaml:
<TextBox Name = "NumAccounts"
Text="{Binding Path = DisplayNumFilter, Mode=TwoWay}" />
Here is a snippet of my c# code:
private string _displayNumFilter;
public string DisplayNumFilter{
get => _displayNimFilter;
set{
_displayNumFilter = value;
OnPropertyChanged("DisplayNumFilter");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName){
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public constructor(){ //it has a different name, I just use this as an example
DisplayNumFilter = "20";
InitializeComponent();
Binding binding = new Binding();
binding.Path = new PropertyPath("DisplayNumFilter");
binding.Source = NumAccounts;
BindingOperations.SetBinding(NumAccounts, TextBox.TextPoperty, binding);
NumAccounts.Text = DisplayNumFilter;
}

The XAML markup Text="{Binding Path=DisplayNumFilter}" tries to bind to a DisplayNumFilter of the current DataContext of the TextBox control so you need to set the DataContext to an instance of the class where the DisplayNumFilter is defined.
This means that your constructor should look something like this:
public constructor() {
DisplayNumFilter = "20";
InitializeComponent();
DataContext = this;
}
There is no reason to create a Binding object programmatically if you use setup the bindings in the XAML markup.

There are some issues with your code, but I will focus on the main:
Your binding source is wrong. From that the binding source it will start with the property path. To resolve the property DisplayNumFilter the source has to be set to this.
public SomeWindow()
{
DisplayNumFilter = "20";
InitializeComponent();
Binding binding = new Binding();
binding.Path = new PropertyPath("DisplayNumFilter");
binding.Source = this; // -> was before NumAccounts;
BindingOperations.SetBinding(NumAccounts, TextBox.TextPoperty, binding);
NumAccounts.Text = DisplayNumFilter;
}

Related

How To Bind Image Source in C# (MVVM)

In My Project,I Create Some Images with C# and i want these photo's sources, Bind to a property in My ViewModel.
in My MVVM :
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private string _Light= "dark.png";
public string Light
{
get { return _Light; }
set {
_Light = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Light)));
}
}
in My C#:
BindingContext = new LightViewModel();
LightViewModel light = new LightViewModel();
Image dark = new Image { Margin = new Thickness(0, -5, 0, 10), HeightRequest = 20, WidthRequest = 20 };
dark.SetBinding(Image.SourceProperty, light.Light);
i use exactly this MVVM with this Xaml, and it's Property work
<Image Source="{Binding Light}" ></Image>
Can help me :)
Actually, the second parameter of SetBinding method is the name of the property, not the property itself, so what you should do is something like this:
dark.SetBinding(Image.SourceProperty, "Light");

WPF Binding Image Source

maybe stupid question, but I don't know anymore...
I have ViewModel class like this:
public class MainWindowsViewModel : INotifyPropertyChanged
{
private ImageSource _img;
public ImageSource StatusImage
{
get { return _img; }
set
{
_img = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Binding in XAML looks like this:
<Window.DataContext>
<VM:MainWindowsViewModel />
</Window.DataContext>
<Image x:Name="gui_image_status" HorizontalAlignment="Left" Height="26" Margin="144,10,0,0" VerticalAlignment="Top" Width="29" Source="{Binding Path=StatusImage}" />
And I set content of ImageSource like this:
MainWindowsViewModel _view = new MainWindowsViewModel();
var yourImage = new BitmapImage(new Uri(String.Format("Sources/{0}.png", "red"), UriKind.Relative));
_view.StatusImage = yourImage;
But it does not work. I think that problem is in that NotifyPropertyChanged, because I tried place brake point in the set and get. Get triggered few times at the start, after then set triggered as well with correct ImageSource, but after then get did not triggered anymore. Like no setting ever happened.
It's really simply binding that I have done many times similarly...I don't know why it doesn't work this time.
You are creating two instances of your MainWindowsViewModel class, one in XAML by
<Window.DataContext>
<VM:MainWindowsViewModel />
</Window.DataContext>
and one in code behind by
MainWindowsViewModel _view = new MainWindowsViewModel();
So your code behind sets the property on a different view model instance than the one the view is bound to.
Change your code behind to this:
var viewModel = (MainWindowsViewModel)DataContext;
viewModel.StatusImage = new BitmapImage(...);
I didn't find any problems in your code, but you can try to check few things.
Check that your Image added to the project and set build action of images to Content (copy if newer).
Before updating ImageSource call Freeze method to prevent error: "Must create DependencySource on same Thread as the DependencyObject"
var yourImage = new BitmapImage(new Uri(String.Format("Sources/{0}.png", "red"), UriKind.Relative));
yourImage.Freeze();
_view.StatusImage = yourImage;
Also, there is an easier way to bind image in WPF. You can use string as a source and set a resource path to the binded property:
public string StatusImage
{
get { return "/AssemblyName;component/Sources/red.png"; }
}

Apply similar binding to multiple WPF controls

Apologies in advance if this has already been asked; I have spent a while Googling and searching stack overflow - but can't find a similar question.
I have a WPF window which has many, many data entry controls. All the controls have two way bindings to the view model, and validate using IDataErrorInfo.
An example of one the bindings is given here:
<TextBox >
<TextBox.Text>
<Binding Path="Street" Mode="TwoWay" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
The only difference between this binding an all the others is the Path.
All my bindings require the validation rule, and the instruction about when to update as there is a lot of cross field validation going on.
My question is - can I apply the same binding to a textbox without all the copy/pasting I am currently having to do for the above example?
I was thinking that maybe I should roll my own subclass of binding to take care of it - but I have no idea if this is good practice or not?
Update:
I've just tried a subclass of the binding like so:
public class ExceptionValidationBinding : Binding
{
public ExceptionValidationBinding()
{
Mode = BindingMode.TwoWay;
NotifyOnValidationError = true;
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
ValidatesOnDataErrors = true;
ValidationRules.Add(new ExceptionValidationRule());
}
}
which makes my xaml look like this:
<TextBox Text="{bindings:ExceptionValidationBinding Path=PostCode}" />
And it seems to work... like I said - no idea if there are any problems with this approach though.
If you don't want any code in your view's code-behind, you can create a MarkupExtension and use it in your XAML. Here's an example of the MarkupExtension:
public class MyBinding : MarkupExtension
{
public string ThePath { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
Binding binding = new Binding(ThePath) {
Mode = BindingMode.TwoWay,
NotifyOnValidationError = true,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
ValidatesOnDataErrors = true
};
binding.ValidationRules.Add(new ExceptionValidationRule());
return binding;
}
}
Then you can do this in your XAML:
<TextBox Text="{local:MyBinding ThePath=Street}">
Edit:Looks like this gives a runtime error. Change the line return binding; to return binding.ProvideValue(serviceProvider);. I guess deriving a class from Binding is actually better, but I will leave this here anyway :)
Even if you're using MVVM, you can apply bindings from the code-behind (as it is still pure-view logic). "Templating" bindings from within XAML, as far as I know, is not possible.
To do it from your code-behind, like so:
void InitBinding() {
Address bindingSource = ...
String[] paths = new String[] { "Street", "City", etc... };
foreach(TextBox textBox in ...) {
String path = ... // get the path for this textbox
Binding binding = new Binding( path );
binding.Source = bindingSource;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding( textBox, TextBox.TextProperty, binding );
}
}
For posterity - the answer I've gone for was my own first update.
This is because there appear to be no side effects to subclassing a binding, and this lets me over-ride any defaults I set on the binding using straight XAML which is something I can't do (without a hell of a lot of work) using the mark up extension idea.
Thank you all who took the time to help me out with this.
My Answer
I've just tried a subclass of the binding like so:
public class ExceptionValidationBinding : Binding
{
public ExceptionValidationBinding()
{
Mode = BindingMode.TwoWay;
NotifyOnValidationError = true;
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
ValidatesOnDataErrors = true;
ValidationRules.Add(new ExceptionValidationRule());
}
}
which makes my xaml look like this:
<TextBox Text="{bindings:ExceptionValidationBinding Path=PostCode}" />
And it seems to work...

How do I access a Label object, given the TextBox bound its Content property?

My objective is to include the Label's text in an error message if the content of the Label's TextBox is not valid. During validation, when only the TextBox object is easily obtained, I would like to obtain the reference to the Label object which has had its Target property bound to that TextBox.
In other words, given the source of a binding, I would like to return or retrieve the target of that binding. The WPF BindingOperations.GetBindingExpression() and related methods require that the target object be known already.
In WPF XAML I have this:
<Label Target="{Binding ElementName=RatingTextBox}">_Rating:</Label>
<TextBox Name ="RatingTextBox"/>
In C# code-behind I tried this:
BindingExpression be = RatingTextBox.GetBindingExpression(TextBox.TextProperty);
string format = be.ParentBinding.StringFormat;
However, be.ParentBinding above is null even though my TextBox is definitely bound by the label because the hot key "[Alt]-R" works. Can my TextBox get that Label's text somehow from the C# code-behind?
If I understand correctly, you are looking for a way to automatically bind the Tooltip property of your TextBox to the Content property of whatever Label object the TextBox is a target of.
Unfortunately, to do this most easily would require a mechanism in WPF to, given the source of a binding, identify its target (or targets…a single source can be bound to multiple targets, of course). And as far as I know, no such mechanism exists as such.
However, I can think of at least a couple of different alternatives that should accomplish a similar effect:
When initializing the window, enumerate all the Label objects to find their targets, and update the targets' Tooltip properties accordingly. Either just set them explicitly, or bind the properties to the Label.Content property.
Reverse the direction the Label target is declared. I.e. create an attached property that can be used on the TextBox object, indicating which Label should target it. Then use this attached property to initialize the Tooltip property appropriate (e.g. in the attached property code, bind or set the Tooltip property, or have some other property that is also bound to the attached property and when it changes, handle the binding or setting there).
The motivation for using an attached property in the second option is to allow the label/target relationship to still be declared just once in the XAML (i.e. avoiding redundancy). It's just that the declaration occurs in the target object (i.e. the TextBox) instead of the label object.
Here are a couple of examples showing what I mean…
First option above:
XAML:
<Window x:Class="TestSO32576181BindingGivenSource.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:TestSO32576181BindingGivenSource"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label x:Name="label1" Content="_Label:" Target="{Binding ElementName=textBox1}"/>
<TextBox x:Name="textBox1"/>
</StackPanel>
</StackPanel>
</Window>
C#:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
InitTooltips(this);
}
private void InitTooltips(FrameworkElement element)
{
foreach (FrameworkElement child in
LogicalTreeHelper.GetChildren(element).OfType<FrameworkElement>())
{
Label label = child as Label;
if (label != null)
{
BindingExpression bindingExpression =
BindingOperations.GetBindingExpression(label, Label.TargetProperty);
if (bindingExpression != null)
{
TextBox textBox =
FindName(bindingExpression.ParentBinding.ElementName) as TextBox;
if (textBox != null)
{
// You could just set the value, as here:
//textBox.ToolTip = label.Content;
// Or actually bind the value, as here:
Binding binding = new Binding();
binding.Source = label;
binding.Path = new PropertyPath("Content");
binding.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(
textBox, TextBox.ToolTipProperty, binding);
}
}
}
InitTooltips(child);
}
}
}
Second option above:
XAML:
<Window x:Class="TestSO32576181BindingGivenSource.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:TestSO32576181BindingGivenSource"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<StackPanel Orientation="Horizontal">
<!-- Note that the Target property is _not_ bound in the Label element -->
<Label x:Name="label1" Content="_Label:"/>
<!-- Instead, it's specified here via the attached property: -->
<TextBox x:Name="textBox1" l:TooltipHelper.TargetOf="{Binding ElementName=label1}"/>
</StackPanel>
</StackPanel>
</Window>
C#:
static class TooltipHelper
{
public static readonly DependencyProperty TargetOfProperty =
DependencyProperty.RegisterAttached("TargetOf", typeof(Label),
typeof(TooltipHelper), new PropertyMetadata(null, _OnTargetOfChanged));
public static void SetTargetOf(FrameworkElement target, Label labelElement)
{
target.SetValue(TargetOfProperty, labelElement);
}
public static Label GetTargetof(FrameworkElement target)
{
return (Label)target.GetValue(TargetOfProperty);
}
private static void _OnTargetOfChanged(
DependencyObject target, DependencyPropertyChangedEventArgs e)
{
Label oldLabel = (Label)e.OldValue,
newLabel = (Label)e.NewValue;
if (oldLabel != null)
{
BindingOperations.ClearBinding(oldLabel, Label.TargetProperty);
BindingOperations.ClearBinding(target, FrameworkElement.ToolTipProperty);
}
if (newLabel != null)
{
Binding binding = new Binding();
binding.Source = newLabel;
binding.Path = new PropertyPath("Content");
binding.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(
target, FrameworkElement.ToolTipProperty, binding);
binding = new Binding();
binding.Source = target;
binding.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(
newLabel, Label.TargetProperty, binding);
}
}
}
Note that in the second option, no new code is required in the window class. Its constructor can just call InitializeComponent() as usual and that's it. All of the code-behind winds up in the TooltipHelper class, which is referenced in the XAML itself.

WPF Binding Issue - UI Updates, Object Does Not

I'm having yet another WPF binding issue. Just when I think I've got this stuff figured out, I run into more problems... :S
Anyway... I've created a custom user control for selecting files. It's a simple textbox followed by a button contained within a grid. The property of the control with which I am working is called FilePath and the TextBox on this control is bound to that property. When the button is clicked, a SaveFileDialog is opened and the user selects a file. The UI correctly updates after the user selects the file.
The problem I seem to be having is that when I bind an object to the control (in this instance I have an object with a DocumentFilePath property) the object doesn't update when a new file is selected.
Here's the relevant code within my user control:
public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register("FilePath", typeof(string), typeof(FileSave), new UIPropertyMetadata(string.Empty, OnFilePathChanged));
public string FilePath
{
get
{
return this.GetValue(FilePathProperty) as string;
}
set
{
this.SetValue(FilePathProperty, value);
this.OnPropertyChanged("FilePath");
}
}
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
private static void OnFilePathChanged(object sender, DependencyPropertyChangedEventArgs e)
{
((FileSave)sender).OnPropertyChanged("FilePath");
}
And the user control is added into my Window programatically by using reflection on my object:
private void AddFileSave(PropertyInfo pi)
{
FileSave fs = new FileSave();
Binding b = new Binding(pi.Name);
fs.SetBinding(FileSave.FilePathProperty, b);
this.AddToGrid(fs); //adds the control into my window's grid in the correct row and column; nothing fancy here
}
It may be worth noting that if I load the window with an existing object, my user control displays properly but still won't register any changes within the object to which it is bound.
Please let me know if you guys need any more info.
Thanks in advance,
Sonny
EDIT: I've found a way around the problem, but this probably isn't a good solution. By watching the debugger carefully I found that when I set the FilePath property within my control, the object was being unbound. If anyone can shed some light on that, I would be most appreciative. In the mean time, I've changed the code that opens my SaveFileDialog to look like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
Microsoft.Win32.OpenFileDialog ofd = new Microsoft.Win32.OpenFileDialog();
ofd.Multiselect = false;
ofd.Title = "Select document to import...";
ofd.ValidateNames = true;
ofd.ShowDialog();
if (this.GetBindingExpression(FilePathProperty) == null)
{
this.FilePath = ofd.FileName;
}
else //set value on bound object (THIS IS THE NEW PORTION I JUST ADDED)
{
BindingExpression be = this.GetBindingExpression(FilePathProperty);
string propName = be.ParentBinding.Path.Path;
object entity = be.DataItem;
System.Reflection.PropertyInfo pi = entity.GetType().GetProperty(propName);
pi.SetValue(entity, ofd.FileName, null);
}
if (!string.IsNullOrWhiteSpace(this.FilePath))
{
_fileContents = new MemoryStream();
using (StreamReader sr = new StreamReader(this.FilePath))
{
_fileContents = new MemoryStream(System.Text.ASCIIEncoding.ASCII.GetBytes(sr.ReadToEnd()));
}
}
else
{
_fileContents = null;
}
}
You're not specifying anywhere in your code that the FilePath property should be TwoWay so updates of the DP value won't get pushed to the bound source object's property. You can use either:
Binding b = new Binding(pi.Name){ Mode = BindingMode.TwoWay };
or you can set up your Dependency Property to use a default of TwoWay:
public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register(
"FilePath", typeof(string), typeof(FileSave),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnFilePathChanged));
You should also follow Robert's suggestion of removing the manual PropertyChange event, and also don't EVER add any code other than GetValue and SetValue in your DP wrapper property. XAML calls GetValue and SetValue directly so will skip over anything else you add there - which can lead to very nasty bugs.
Why, yes! I most certainly can shed some light on that!
Also, if you're using .Net 4.0, today's your lucky day!
Consider the following fine method on your DependencyObject:
SetCurrentValue();
Yes! With this SINGULAR method, all your woes will drift away as a bad dream at the rooster's crow! (Well, ok, not really, but that is the method you're looking for.)
Short story very short: When you programmatically SetValue() on a control in your view layer, you blow away your bindings. SetCurrentValue() was added to the framework because you frequently want to drive a change in your bound object by setting that value directly. An alternate design would be to set the value in your bound object programmatically and let the updated value get pulled back into the view, but that's frequently clumsy.
(I strongly suspect that the absence of this method up to this point is largely responsible for the utter failure of the vast majority of NumericUpDown controls in WPF.)
First, you don't need to raise the PropertyChanged event when a dependency property changes; with dependency properties, change notification comes for free.
What's probably happening here: The default behavior for UpdateSourceTrigger is LostFocus, i.e. the source gets updated when the user presses TAB to move to the next field, or clicks on another control, or whatever. The text box isn't losing focus after your SaveFileDialog sets Text (since it probably doesn't even have the focus in the first place), so the source update never gets triggered.
To make it update the source whenever the Text property changes, set the UpdateSourceTrigger to PropertyChanged.
If that doesn't work, watch the Output window for binding errors.
Edit:
Here's a little prototype application I built. It works just fine: typing in the text box sets the property, clicking on the "Save" button sets the property, and the binding in the main window gets updated properly no matter what.
<Window x:Class="DependencyPropertyBindingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:demo="clr-namespace:DependencyPropertyBindingDemo"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<demo:FilePicker x:Name="Picker"
DockPanel.Dock="Top"
Margin="5" />
<TextBox DockPanel.Dock="Top"
Text="{Binding ElementName=Picker, Path=FilePath}" />
<TextBlock />
</DockPanel>
</Window>
<UserControl x:Class="DependencyPropertyBindingDemo.FilePicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel>
<TextBox DockPanel.Dock="Left"
Width="200"
Text="{Binding FilePath, UpdateSourceTrigger=PropertyChanged}" />
<Button Width="50"
DockPanel.Dock="Left"
Command="{Binding Path=SaveCommand}">Save</Button>
<TextBlock />
</DockPanel>
</UserControl>
public partial class FilePicker : UserControl
{
public FilePicker()
{
SaveCommand = new FilePickerSaveCommand(this);
DataContext = this;
InitializeComponent();
}
public ICommand SaveCommand { get; set; }
public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register("FilePath", typeof(string), typeof(FilePicker));
public string FilePath
{
get
{
return GetValue(FilePathProperty) as string;
}
set
{
SetValue(FilePathProperty, value);
}
}
}
public class FilePickerSaveCommand : ICommand
{
private FilePicker _FilePicker;
public FilePickerSaveCommand(FilePicker picker)
{
_FilePicker = picker;
}
public void Execute(object parameter)
{
_FilePicker.FilePath = "Testing";
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}

Categories