2010-12-20 59 views
4

我有另一个WPF绑定问题。正当我想我已经弄清了这些东西时,我遇到了更多问题......:SWPF绑定问题 - UI更新,对象没有

无论如何...我已经创建了一个用于选择文件的自定义用户控件。这是一个简单的文本框,后面跟着一个网格中包含的按钮。我正在使用的控件的属性被称为FilePath,并且此控件上的TextBox绑定到该属性。点击按钮后,会打开一个SaveFileDialog,用户选择一个文件。在用户选择文件后,用户界面正确更新。

我似乎遇到的问题是,当我将一个对象绑定到控件(在这种情况下,我有一个具有DocumentFilePath属性的对象)时,对象不会在选择新文件时更新。

这里是我的用户控件中的相关代码:

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"); 
} 

而且用户控件编程用我的物体上反射添加到我的窗口:

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 
} 

这可能是值得指出的是,如果我用现有的对象加载窗口,我的用户控件将正确显示,但仍不会在其绑定的对象内注册任何更改。

如果你们需要更多信息,请让我知道。

由于提前,
桑尼

编辑:我找到解决问题的方式,但是这可能不是一个很好的解决方案。通过仔细观察调试器,我发现当我在我的控件中设置FilePath属性时,对象被解除绑定。如果有人能够说明这一点,我会非常感激。在此同时,我已经改变了我开的SaveFileDialog代码看起来像这样:

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; 
    } 
} 

回答

3

你不是在你的代码的任何地方指定FilePath属性应该是TwoWay,所以DP值的更新不会被推送到绑定的源对象的属性。您可以使用:

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

,或者你可以设置你的依赖项属性使用双向的默认:

public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register(
"FilePath", typeof(string), typeof(FileSave), 
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnFilePathChanged)); 

,也应该遵循删除手工PropertyChange事件的罗伯特的建议,也不要不要在您的DP包装器属性中添加除GetValue和SetValue以外的任何代码。 XAML直接调用GetValue和SetValue,因此会跳过你在其中添加的任何内容 - 这可能导致非常讨厌的错误。

+0

谢谢。那样做了。 – 2010-12-21 23:51:43

1

首先,你不需要提高PropertyChanged事件时,一个依赖属性的变化;与依赖属性,更改通知免费。

这里可能发生了什么:UpdateSourceTrigger的默认行为是LostFocus,即当用户按TAB移动到下一个字段,或者点击另一个控件或其他控件时,源被更新。在你的SaveFileDialog设置Text(因为它可能甚至没有把重点放在第一位)之后,文本框不会失去焦点,所以源更新从不被触发。

为了使Text属性更改时更新源,请将UpdateSourceTrigger设置为PropertyChanged

如果这样不起作用,请查看输出窗口中的绑定错误。

编辑:

这里有一个小的应用程序原型我建。它工作得很好:在文本框中输入设置属性,单击“保存”按钮设置属性,无论如何,主窗口中的绑定都会正确更新。

<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; 
} 
+0

谢谢,罗伯特。尽管没有任何选择。我找到了一种解决方法,也许它可以揭示什么问题?请在原文中查看我的编辑。 – 2010-12-20 21:46:57

+0

罗伯特,关于你的编辑,ICommand服务器有什么用途?我的用户控件没有。 – 2010-12-21 00:02:23

2

为什么,是的!我当然可以阐明这一点!

此外,如果您使用.Net 4.0,今天是您的幸运日!

考虑以下罚款的方法对你的DependencyObject:

SetCurrentValue();

是的!用这种奇异的方法,你所有的苦难都会在公鸡的乌鸦身上飘过去,成为一场噩梦! (嗯,好的,不是真的,但这是你正在寻找的方法。)

短故事很短:当你在视图层的控件上编程SetValue()时,你吹走了你的绑定。 SetCurrentValue()被添加到框架中,因为您经常希望通过直接设置该值来驱动绑定对象中的更改。另一种设计是以编程方式设置绑定对象中的值,并让更新后的值返回到视图中,但这通常很笨拙。

(我强烈怀疑,如果没有这个方法到这一点的是在WPF绝大多数的NumericUpDown控制的彻底失败的主要原因。)

+0

他在自己的代码中做了些什么,我不是在做我的?为什么我的'FilePickerSaveCommand'中的代码没有吹走绑定? – 2010-12-20 22:15:47

+0

现在我的绑定被维护,但绑定对象仍然没有得到更新的新值。 – 2010-12-20 23:26:35