WPF Binding Error - UI Updates, Object Not

I am having another issue with WPF binding. Just when I think that everything worked out for me, I have more problems ...: S

Anyway ... I created a user control to select files. This is a simple text box followed by a button contained in a grid. The property of the control I'm working with is called FilePath, and the TextBox on this control is bound to this property. When the button is pressed, SaveFileDialog opens and the user selects the file. The user interface is updated correctly after the user selects a file.

The problem that I seem to encounter is that when I attach the object to the control (in this case I have an object with the DocumentFilePath property), the object does not update when a new file is selected.

Here is the relevant code in 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 to my window programmatically 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 grid in the correct row and column; nothing fancy here } 

It may be worth noting that if I load a window with an existing object, my user control will be displayed correctly, but it still won’t register any changes in the object to which it is attached.

Please let me know if you need more information.

Thanks in advance,
Sonny

EDIT: I found a way to solve this problem, but this is probably not a very good solution. Watching the debugger carefully, I found that when I set the FilePath property to my control, the object was unbound. If anyone can shed light on this, I will be very grateful. At the same time, I changed the code that my SaveFileDialog opens 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; } } 
+4
source share
3 answers

You do not indicate anywhere in your code that the FilePath property should be TwoWay, therefore updates to the DP value will not be transferred to the property of the associated source object. You can use either:

 Binding b = new Binding(pi.Name){ Mode = BindingMode.TwoWay }; 

or you can configure the Dependency property to use the default value for 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 recommendations for removing the PropertyChange event manually, and DO NOT add code other than GetValue and SetValue to the DP shell property. XAML directly calls GetValue and SetValue, so it will skip anything you add there, which can lead to very unpleasant errors.

+3
source

Why yes! I can certainly shed some light on this!

Also, if you are using .Net 4.0, today is your lucky day!

Consider the following fine method on your DependencyObject:

SetCurrentValue();

Yes! With this SINGULAR method, all your troubles will go away, like a bad dream with a rooster! (Well, fine, not really, but this is the method you are looking for.)

The short story is very short: when you programmatically SetValue() on a control in your view layer, you remove your bindings. SetCurrentValue () was added to the structure because you often want to control how a related object changes by setting this value directly. An alternative design would be to set the value in the associated object programmatically and allow the updated value to return to viewing, but this is often clumsy.

(I strongly suspect that the absence of this method until this point is largely responsible for the complete failure of the vast majority of NumericUpDown controls in WPF.)

+2
source

First, you do not need to raise the PropertyChanged event when the dependency property changes; with dependent properties, notification of changes is provided free of charge.

What is probably happening here: The default behavior for UpdateSourceTrigger is LostFocus , meaning the source is updated when the user presses TAB to go to the next field or clicks on another control or something else. The text field does not lose focus after your SaveFileDialog sets the Text (since it probably does not even have focus in the first place), so the original update never starts.

To update the source when the Text property changes, set UpdateSourceTrigger to PropertyChanged .

If this does not work, see the output window for binding errors.

Edit:

Here is a small prototype of the application that I built. It works fine: entering text into the text field sets the property, clicking the "Save" button sets the property, and the binding in the main window is 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; } 
+1
source

Source: https://habr.com/ru/post/1332808/


All Articles