用户控件属性绑定未更新DataContext

3
我创建了一个简单的可重复使用控件来浏览文件。
然后我创建了一个模型并实现了OnPropertyChanged,并在MainWindow中使用了该控件。
当我点击UserControl中的“浏览”按钮时,依赖属性“FilePath”被正确设置(文本框也获取了路径字符串),但模型似乎没有起作用。
额外信息:如果我使用普通文本框而不是UserControl并执行...
<TextBox Text="{Binding InputFile}"/>

当我在框中输入内容时,模型更新正确。

如果我手动在用户控件中输入东西(而不是通过浏览按钮填充),它仍然无法工作。

这是 UserControl 的代码,属性在控件内部正确设置:

public partial class FileBrowserTextBox : UserControl
{
    public FileBrowserTextBox()
    {
        InitializeComponent();
    }

    // FilePath
    public static readonly DependencyProperty FilePathProperty =
        DependencyProperty.Register("FilePath", typeof(string), typeof(FileBrowserTextBox), new FrameworkPropertyMetadata(string.Empty, new PropertyChangedCallback(OnFilePathPropertyChanged)));

    public string FilePath
    {
        get { return (string)GetValue(FilePathProperty); }
        set { SetValue(FilePathProperty, value); }
    }
    static void OnFilePathPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        var obj = o as FileBrowserTextBox;
        if (obj == null)
            return;
    }
    private void BrowseButton_Click(object sender, RoutedEventArgs e)
    {
        // Create OpenFileDialog 
        Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
        // Set filter for file extension and default file extension 
        dlg.DefaultExt = ".txt";
        dlg.Filter = "TXT Files (*.txt)|*.txt|All Files (*.*)|*.*";
        // Display OpenFileDialog by calling ShowDialog method 
        Nullable<bool> result = dlg.ShowDialog();
        // Get the selected file name and display in a TextBox 
        if (result == true)
        {
            // Open document 
            string filename = dlg.FileName;
            FilePath = filename; // this works and updates the textbox
        }
    }
}

以下是XAML代码片段:

<UserControl x:Class="DrumMapConverter.FileBrowserTextBox">
        ...
        <Button Content=" ... " Click="BrowseButton_Click"/>
        <TextBox Name="txtFilepath" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=FilePath}"/>
        ...
</UserControl>

这是我在使用INotifyPropertyChanged时所采用的模型:
public class DrumMapConverterDataModel :INotifyPropertyChanged
{
    public string InputFile
    {
        get { return inputFile; }
        set
        {
            inputFile = value;
            OnPropertyChanged("InputFile");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    // Create the OnPropertyChanged method to raise the event 
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

    private string inputFile;
}

这是MainWindow类

public partial class MainWindow : Window
{
    private DrumMapConverterDataModel model;
    public MainWindow()
    {
        InitializeComponent();
        model = new DrumMapConverterDataModel();
        DataContext = model;
    }

    private void Button_Click_1(object sender, RoutedEventArgs e)
    {
        MessageBox.Show(model.InputFile); // if i break here the model has no data changed (InputFile is null)
    }
}

这是我如何使用两个控件的方法(两个示例都无法正常工作):
<cust:FileBrowserTextBox  Label="Input File" FilePath="{Binding InputFile}"/>
<cust:FileBrowserTextBox  Label="Input File" FilePath="{Binding Path=InputFile, Mode=TwoWay, 
                       RelativeSource={RelativeSource FindAncestor, 
                           AncestorType=Window}}"/>

非常感谢您的帮助。

更新和解决方案:

如@AnatoliyNikolaev所建议(感谢他对UpdareSourceTrigger的解释)和@Karuppasamy在用户控件中的建议,可以像这样完成(明确使用UpdateSourceTrigger,因为它是一个文本框):

<TextBox Grid.Column="2" Name="txtFilepath" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=FilePath, UpdateSourceTrigger=PropertyChanged}"/>

然后,DependencyProperty 可以像这样(请注意 BindsTwoWayByDefault):
public static readonly DependencyProperty FilePathProperty =
    DependencyProperty.Register("FilePath", typeof(string), typeof(FileBrowserTextBox), new FrameworkPropertyMetadata(string.Empty, new PropertyChangedCallback(OnFilePathPropertyChanged)) { BindsTwoWayByDefault = true });

最后在主窗口中,我可以简单地编写以下代码:

<cust:FileBrowserTextBox  FilePath="{Binding Path=InputFile}" />
2个回答

4
尝试将你的依赖属性设置UpdateSourceTrigger设置为PropertyChanged

默认值是Default,它返回目标依赖属性的默认UpdateSourceTrigger值。但是,大多数依赖属性的默认值是PropertyChanged,而Text属性的默认值是LostFocus

例如:

<local:FileBrowserTextBox FilePath="{Binding Path=InputFile, 
                                             Mode=TwoWay,
                                             UpdateSourceTrigger=PropertyChanged}" />

@Domenico Alessi:如果它能工作,为什么我的答案没有被接受?“如果我在UserControl本身中使用UpdateSourceTrigger,它不起作用,在MainWindow中是可以的。”- 但你接受了你没有帮助的答案。 - Anatoliy Nikolaev
最终我使用了第二个答案,在UserControl中添加代码更加简洁,而且在MainWindow中的输入量也较少。我在问题本身中发布了我解决它的方法。我在控件中使用了UpdateSourceTrigger,所以我认为接受那个回复是合适的。我可以接受你的回复,但那不是我使用的方法。 - powernemo

2

我希望我能理解你的问题,

我尝试过同样的事情,并在用户控件的XAML代码中绑定TextBox时将UpdateSourceTrigger设置为PropertyChanged。以下是我的代码,

 <TextBox Width="200" Name="txtFilepath" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=FilePath, UpdateSourceTrigger=PropertyChanged}"/>
    <Button Width="40" Content=" ... " Click="BrowseButton_Click"/>

它正在运作。

谢谢


你为什么要写同样的东西,我已经在我的回答中写过了? - Anatoliy Nikolaev
@AnatoliyNikolaev 我已经提到了将 UpdateSourceTrigger 添加到 UserControl 本身,而不是在使用时添加(正如你所说的)。我认为在使用时添加 UpdateSourceTrigger 是无效的。 - Karuppasamy
“我认为在使用时添加UpdateSourceTrigger是无效的。” - 经过测试,它是有效的。 - Anatoliy Nikolaev
如果我在UserControl本身中使用UpdateSourceTrigger,它不起作用,在MainWindow中是可以的。在MainWindow和UserControl中,正确的绑定表达式是什么,以使其正常工作? - powernemo
好的,我发现这个代码可以放到用户控件中:<TextBox Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=FilePath, UpdateSourceTrigger=PropertyChanged}"/>,而这个代码可以用于主窗口:<cust:FileBrowserTextBox Label="Input File" FilePath="{Binding Path=InputFile, Mode=TwoWay}" />。现在的问题是是否有办法去除双向绑定模式? - powernemo
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接