Question

I am trying to make a very easy UserControl that has a path that you can type in a textbox or that you can find by clicking a browse button. I tried to do this with a dependency property but this doesn't work completely when binding to it.

Here my xaml:

<UserControl x:Class="PathSelector.PathSelector"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:PathSelector">
    <DockPanel Height="28">
        <Button DockPanel.Dock="Right" Padding="5" Margin="5 0 0 0"
                FontWeight="Bold"
                Content="..."
                Click="BrowseButton_Click" />
        <Grid>
            <TextBox 
                HorizontalAlignment="Stretch" VerticalAlignment="Center"
                x:Name="SelectedPathTxtBox"
                LostKeyboardFocus="SelectedPathTxtBox_LostKeyboardFocus" />
        </Grid>        
    </DockPanel>
</UserControl>

And this is the codebehind:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;

namespace PathSelector
{
    /// <summary>
    /// A simple input for path, with browse button
    /// </summary>
    public partial class PathSelector : UserControl
    {
        public PathSelector()
        {
            InitializeComponent();
        }


        private void BrowseButton_Click(object sender, RoutedEventArgs e)
        {
            System.Windows.Forms.OpenFileDialog fileDialog = new System.Windows.Forms.OpenFileDialog();
            fileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            if (fileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                SelectedPathTxtBox.Text = fileDialog.FileName;
            }
        }  

        #region Dependency Properties

        public string SelectedPath
        {
            get { return (string)GetValue(SelectedPathProperty); }
            set { SetValue(SelectedPathProperty, value); }
        }

        public static readonly DependencyProperty SelectedPathProperty =
            DependencyProperty.Register(
            "SelectedPath", 
            typeof(string), 
            typeof(PathSelector), 
            new FrameworkPropertyMetadata(new PropertyChangedCallback(SelectedPathChanged))
                {
                    BindsTwoWayByDefault = true,
                    DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                });

        private static void SelectedPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            MessageBox.Show("Changed!");
            // How to update the values here??
        }

        #endregion             

        private void SelectedPathTxtBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            SelectedPath = SelectedPathTxtBox.Text;
        }
    }
}

I want to use this UserControl like this later:

<pathselector:PathSelector 
     SelectedPath="{Binding PathToSomeFile}"/>

"PathToSomeFile" is a string variable in the ViewModel that should be updated in both directions.

How can I achieve this? What am I missing?

Thanks a lot!

Was it helpful?

Solution

Modify SelectedPathChanged as below:

private static void SelectedPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ((PathSelector)d).SelectedPathTxtBox.Text = e.NewValue.ToString();

    MessageBox.Show("Changed!");
}

OTHER TIPS

You should bind TextBox Text to your custom DP which will automatically update its source property.

<TextBox HorizontalAlignment="Stretch" VerticalAlignment="Center"
         x:Name="SelectedPathTxtBox"
         Text="{Binding SelectedPath, RelativeSource={RelativeSource
                             Mode=FindAncestor, AncestorType=UserControl}}"/>

Also you don't need to handle LostFocus, since Text default UpdateSourceTrigger value is LostFocus. It will update the binding property SelectedPath on lost focus.

And since SelectedPath, default UpdateSourceTrigger value is PropertyChanged, it will update PathToSomeFile whenever property changes.

If you just miss the both direction part, you can use:

<pathselector:PathSelector SelectedPath="{Binding PathToSomeFile, Mode=TwoWay}" />

More info here:

MSDN Binding.Mode Property

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top