简体   繁体   中英

WPF TextBox Binding does not update with Drag and Drop

I have a wpf project where I have a bound text box that has AllowDrop set to true. If I type directly into this text box and leave the box, the bound property for this text box changes as expected.

However, if I create a drop event and set the text value for the text box to the filename value, the text box bound property does not change. I have to click into the text box and tab out of it.

I must be misunderstanding how bound properties should work. My thinking was that if the text of the box changes that it should update the bound property as well.

As it stands now, I have to have the code behind update the property rather than rely upon the binding. Below is from a sample project I created.

XAML:
<Window.DataContext>
    <local:XmlFile x:Name="XmlFileInfo"/>
</Window.DataContext>
...
<StackPanel Orientation="Horizontal" Grid.Row="0">
        <Label Content="XML File:"/>
        <TextBox x:Name="xmlFilePath" Text="{Binding XMLFile}" Height="25" VerticalAlignment="Top" MinWidth="300" AllowDrop="True" PreviewDragOver="xmlFilePath_PreviewDragOver" Drop="xmlFilePath_Drop"/>
</StackPanel>

And below is my viewmodel

public class XmlFile
{
    private string _xmlfile;
    private string _xmlElementName;
    public string XMLFile
    {
        get { return _xmlfile; }
        set
        {
            if (value != _xmlfile)
            {
                _xmlfile = value;
                _xmlElementName = SetElementNameFromFileName();
            }
        }
    }
...
}

And finally my code behind for XAML

    public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Exit_Click(object sender, RoutedEventArgs e)
    {
        Application.Current.Shutdown();
    }

    private void SetControlState()
    {
        FileTest.IsEnabled = false;
        if (!string.IsNullOrEmpty(XmlFileInfo.XMLFile))
        {
            if(XmlFileInfo.IsValidXml(XmlFileInfo.XMLFile))
            {
                FileTest.IsEnabled = true;
            }
        }
    }

    private void xmlFilePath_PreviewDragOver(object sender, DragEventArgs e)
    {
        e.Handled = true;
    }

    private void xmlFilePath_Drop(object sender, DragEventArgs e)
    {
        var filenames = (string[])e.Data.GetData(DataFormats.FileDrop);
        if (filenames == null) return;
        var filename = filenames.FirstOrDefault();
        if (filename == null) return;
        //XmlFileInfo.XMLFile = filename; <-- This bypasses the binding
        (sender as TextBox).Text = filename; // This should trigger updating binding
        SetControlState(); //<-- enables a button control if file is valid
    }
}

I have tried setting the binding mode to twoway, and other binding settings without any change in behavior. What I would like to do is figure out how to get the drop functionality to act just like manually typing into the box and leaving the box without having to bypass my binding and directly setting the property.

Make your ViewModel implement INotifyPropertyChanged. Instead of directly changing the text of your textbox change the fileName in your viewModel. That will do the trick.

You also have to call OnPropertyChanged in the setter of your XMLFile-Property

public class XmlFile : INotifyPropertyChanged
{
    private string _xmlfile;
    private string _xmlElementName;
    public string XMLFile
    {
        get { return _xmlfile; }
        set
        {
            if (value != _xmlfile)
            {
                _xmlfile = value;
                _xmlElementName = SetElementNameFromFileName();
                OnPropertyChanged(nameof(XMLFile); //this tells your UI to update
            }
        }
    }
...
}


 private void xmlFilePath_Drop(object sender, DragEventArgs e)
    {
        var filenames = (string[])e.Data.GetData(DataFormats.FileDrop);
        if (filenames == null) return;
        var filename = filenames.FirstOrDefault();
        if (filename == null) return;
        //XmlFileInfo.XMLFile = filename; <-- This bypasses the binding
        var viewModel = (XmlFile)this.DataContext;
        viewModel.XMLFile = filename;
        SetControlState(); //<-- enables a button control if file is valid
    }

You should have a look at how DataBinding works.

What you're looking for is the following:

        var textBox = sender as TextBox;

        if (textBox == null)
            return;

        // Sets the value of the DependencyProperty without overwriting the value, this will also update the ViewModel/XmlFile that TextBox is bound to.
        textBox.SetCurrentValue(TextBox.TextProperty, "My Test Value");

You can also force the binding to update the target(XmlFile) by the following:

        var textBinding = textBox.GetBindingExpression(TextBox.TextProperty);

        // This will force update the target (rather than wait until control loses focus)
        textBinding?.UpdateTarget();

I had a situation where I needed to populate multiple textboxes with filepaths. I used the ideas from akjoshi presented here: Update ViewModel from View . I implemented it in the following way:

private void Text_Drop(object sender, DragEventArgs e)
{
    TextBox temp = sender as TextBox;
    if(temp == null)
        return;
    string[] tempArray = (string[])e.Data.GetData(DataFormats.FileDrop, false);
    temp.Text = tempArray[0];
    sender = temp;
    BindingExpression bind = BindingOperations.GetBindingExpression(temp, TextBox.TextProperty);
    bind.UpdateSource();
}

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM