我还有另一个WPF绑定问题。就在我认为我已经找到了这些东西的时候,我遇到了更多的问题......:S
无论如何......我已经创建了一个用于选择文件的自定义用户控件。它是一个简单的文本框,后跟一个包含在网格中的按钮。我正在使用的控件的属性称为FilePath,此控件上的TextBox绑定到该属性。单击该按钮时,将打开SaveFileDialog,用户选择一个文件。用户选择文件后,UI会正确更新。
我似乎遇到的问题是,当我将一个对象绑定到控件时(在这个例子中我有一个带有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;
}
}
答案 0 :(得分:3)
您没有在代码中的任何位置指定FilePath属性应该是TwoWay,因此DP值的更新不会被推送到绑定的源对象的属性。您可以使用:
Binding b = new Binding(pi.Name){ Mode = BindingMode.TwoWay };
或者您可以设置您的依赖属性以使用默认值TwoWay:
public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register(
"FilePath", typeof(string), typeof(FileSave),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnFilePathChanged));
您还应该遵循Robert关于删除手动PropertyChange事件的建议,也不要在DP包装器属性中添加除GetValue和SetValue之外的任何代码。 XAML直接调用GetValue和SetValue,因此会跳过你在那里添加的任何其他东西 - 这可能会导致非常讨厌的错误。
答案 1 :(得分:2)
为什么,是的!我当然可以对此有所了解!
另外,如果您使用的是.Net 4.0,今天是您的幸运日!
在DependencyObject上考虑以下精细方法:
是的!有了这种奇异的方法,你所有的困境都会在公鸡的乌鸦中作为一个糟糕的梦想而消失! (好吧,不,不是真的,但那是你正在寻找的方法。)
简短的故事非常简短:当您在视图层中的控件上以编程方式SetValue()
时,您就会吹掉绑定。 SetCurrentValue()已添加到框架中,因为您经常希望通过直接设置该值来驱动绑定对象的更改。另一种设计是以编程方式设置绑定对象中的值,并将更新后的值拉回到视图中,但这通常很笨拙。
(我强烈怀疑到目前为止这种方法的缺失是WPF中绝大多数NumericUpDown控件彻底失败的主要原因。)
答案 2 :(得分: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;
}