I have a textblock that is a 'status label'. I want this label to be updated, and when that happens, I want its color to be also set automatically - as well as visibility (Label invisible until it has content).
The problem is, that if I specify anything more than the text binding, then the textblock does not change (i.e. text does not appear, and it is still hidden).
Actually, I tried also without binding visibility, and it appears that the Foreground also blocks the binding.
<TextBlock x:Name="StatusInfo"
HorizontalAlignment="Center" VerticalAlignment="Bottom"
FontSize="9"
Visibility="{Binding ElementName=ThisUc,
Path=StatusLabelVisibility}"
Text="{Binding ElementName=ThisUc,
Path=StatusLabel}"
Foreground="{Binding ElementName=ThisUc,
Path=StatusLabelBrush}" />
This is all in a UserControl, so I am using dependency properties for StatusLabel property, as I want to bind it to properties in main window... Foreground and Visibility properties are not dependency properties, as I don't want to expose them.
This is my property setter and getter:
public string StatusLabel
{
get { return (string)GetValue(StatusLabelProperty); }
set
{
SetValue(StatusLabelProperty, value);
RaisePropertyChanged("StatusLabel");
if (value != string.Empty)
{
StatusLabelVisibility = System.Windows.Visibility.Visible;
if (value.HasAny("success", "ok") && !value.HasAny("partial"))
{
StatusLabelBrush = Brushes.Green;
}
else if (value.HasAny("fail"))
{
StatusLabelBrush = Brushes.DarkRed;
}
else if (value.HasAny("partial"))
{
StatusLabelBrush = Brushes.DarkGoldenrod;
}
else
{
StatusLabelBrush = Brushes.Black;
}
}
else
{
StatusLabelVisibility = System.Windows.Visibility.Collapsed;
}
}
}
Please let me know what am I doing wrong, perhaps that's not the way to go altogether?
Cheers
====================
While Meredith's answer solved the issue, let me just post a comment for future reference (as it was not obvious for me):
Here it goes - if you assign the UserControl property directly, not via property binding, it appears to lose the 'bound' - and if you try to change the bound property again, it won't update the control as it would have before it 'lost the bound'
Cheers
If StatusLabel is a DependencyProperty, you can't put anything else in the setter - it won't get called correctly. Look up the way to do changed events for DependencyProperties instead. You need a PropertyChangedCallback. Check out How to use PropertyChangedCallBack. Raise your prop changes, and set all the other properties in the callback.
Related
As title states, I can't get KeyBinding to work when using property element syntax. By work I mean using the key combo of Ctrl+Del to change the background color of the list box. The key combo can be used or the button can be clicked, both of which invoke the command, yet the command is never invoked. When a breakpoint is set while in debug mode it will never be encountered.
I've followed the InputBinding Class example from the documentation and can only get KeyBinding to work when using a UserControl and would like to understand why that is, and what I'm doing wrong.
Below is an MVCE of when the code, declared with property element syntax, that does not work. Commented out is a line for a UserControl which encapsulates the StackPanel and allows the KeyBinding to work. Contingent on the commenting out each PropertyElementSyntax region and uncommenting each UserControlSyntax region in the code behind for MainWindow.xaml.cs.
MainWindow.xaml:
<Window x:Class="LearningKeyBindingWPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:LearningKeyBindingWPFApp"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="300">
<!--<local:UserControl1 x:Name="CustomColorPicker" />-->
<StackPanel Margin="0,40,0,0">
<StackPanel.InputBindings>
<KeyBinding Command="{Binding ChangeColorCommand}"
CommandParameter="{Binding ElementName=ColorPicker, Path=SelectedItem}"
Key="{Binding ChangeColorCommand.Key}"
Modifiers="{Binding ChangeColorCommand.ModifierKeys}" />
<MouseBinding Command="{Binding ChangeColorCommand}"
CommandParameter="{Binding ElementName=ColorPicker, Path=SelectedItem}"
MouseAction="{Binding ChangeColorCommand.MouseAction}" />
</StackPanel.InputBindings>
<Button Content="Change Color"
Command="{Binding ChangeColorCommand}"
CommandParameter="{Binding ElementName=ColorPicker, Path=SelectedItem}" />
<ListBox Name="ColorPicker"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
SelectedIndex="0">
<sys:String>Red</sys:String>
<sys:String>Green</sys:String>
<sys:String>Blue</sys:String>
<sys:String>Yellow</sys:String>
<sys:String>Orange</sys:String>
<sys:String>Purple</sys:String>
</ListBox>
</StackPanel>
</Window>
Code-behind for MainWindow.xaml.cs:
public MainWindow()
{
DataContext = this;
InitializeComponent();
InitializeCommand();
#region UserControlSyntax
//CustomColorPicker.ColorPicker.Focus();
#endregion
#region PropertyElementSyntax
ColorPicker.Focus();
#endregion
}
public SimpleDelegateCommand ChangeColorCommand { get; private set; }
private SolidColorBrush _originalColor;
private void InitializeCommand()
{
#region UserControlSyntax
//_originalColor = (SolidColorBrush)CustomColorPicker.ColorPicker.Background;
#endregion
#region PropertyElementSyntax
_originalColor = (SolidColorBrush)ColorPicker.Background;
#endregion
ChangeColorCommand = new SimpleDelegateCommand(ChangeColor)
{
Key = Key.Delete,
ModifierKeys = ModifierKeys.Control
};
}
private void ChangeColor(object colorString)
{
if (colorString == null)
{
return;
}
var selectedColor = SelectedColor((string)colorString);
#region UserControlSyntax
//if (CustomColorPicker.ColorPicker.Background == null)
//{
// CustomColorPicker.ColorPicker.Background = selectedColor;
// return;
//}
//CustomColorPicker.ColorPicker.Background = ((SolidColorBrush)CustomColorPicker.ColorPicker.Background).Color == selectedColor.Color
// ? _originalColor
// : selectedColor;
#endregion
#region PropertyElementSyntax
if (ColorPicker.Background == null)
{
ColorPicker.Background = selectedColor;
return;
}
var isColorIdentical = ((SolidColorBrush)ColorPicker.Background).Color == selectedColor.Color;
ColorPicker.Background = isColorIdentical
? _originalColor
: selectedColor;
#endregion
}
private SolidColorBrush SelectedColor(string value)
{
#region UserControlSyntax
//var selectedColor = (Color)ColorConverter.ConvertFromString(value);
#endregion
#region PropertyElementSyntax
var selectedColor = (Color)ColorConverter.ConvertFromString((string)ColorPicker.SelectedItem);
#endregion
return new SolidColorBrush(selectedColor);
}
The problem is that in the no-UserControl scenario, the DataContext is set before the command object has been initialized.
WPF has a robust binding system, but it normally relies on property-change notifications, via INotifyPropertyChanged. Some scenarios will work without that, as long as you get the order of operations correct. But, without property-change notifications, if you miss your window of opportunity to present some property value to WPF, it's not going to try again later.
When you use the UserControl, the initialization of the bindings for the UserControl occurs after you set up the ChangeColorCommand property. This is just an artifact of how WPF initializes the various objects in the UI tree. But it means that by the time the UserControl's bindings look at the ChangeColorCommand property, it has the value you want.
On the other hand, when you put the StackPanel explicitly into the window's XAML, it's too late by the time you set the property for WPF to see it. It already resolved those bindings during the InitializeComponent() call. Setting the property later has no effect.
There are a couple of ways you could address that given the code you have now:
The simplest is to just move the assignment of DataContext = this; to after the call to InitializeCommand(). Updating the DataContext requires WPF to update all of the dependent bindings too, so doing that after the InitializeCommand() call ensures the property has the value you want.
Implement INotifyPropertyChanged in the MainWindow class, and raise the PropertyChanged event for the ChangeColorCommand property when you set it. This will let WPF know that the value changed and that it should re-evaluate any bindings that depended on it.
All that said, I'd go one further:
Implement a proper view model object, with INotifyPropertyChanged and a ChangeColorCommand, and use that as the data context. Making your UI objects do double-duty as both UI and property binding source (i.e. the view model's job) doesn't fit with the normal WPF model, sacrifices the benefits that MVVM would normally provide, and of course introduces this kind of weird timing thing where it's not obvious why a property binding isn't working as expected.
Okay, technically there's a fourth approach you could take, which is to put the call to InitializeCommand() before InitializeComponent(). Main problem with that is, at the moment, it relies on retrieving directly the value of a UI object's property, and that UI object won't exist until after InitializeComponent() is called.
Which brings me back to the #3 option above. Fact is, you shouldn't be accessing UI object properties directly. That should be another property in your view model, and you should make a more direct choice about what that initial color should be, than just grabbing it from the UI on startup.
I admit, there's some wiggle room for design here, but you should be trying to keep your view model and UI code as divorced from each other as possible.
I'm working on my first usercontrol ang got stuck on following problem
Changing the Value of binded property doesn’t even call the property getter (a textbox does)
What is the reason for this behavior?
<luc:IpTextBox x:Name="IpTxtBxIpv4Address"
Grid.Row="1" Grid.Column="1"
IsEnabled="{Binding VmNicData.EnableIpSetting}"
Mode="IPv4Address"/>
<TextBox x:Name="TxtBxTest"
Grid.Row="1" Grid.Column="2"
IsEnabled="{Binding VmNicData.EnableIpSetting}"
Text="{Binding VmNicData.IpV4SubnetMask}"/>
ViewModel:
private bool _ipEnabled;
public bool IpEnabled
{
get => _ipEnabled;
set
{
_ipEnabled = value;
OnPropertyChanged(nameof(IpEnabled));
OnPropertyChanged(nameof(EnableIpSetting));
}
}
private bool _dhcpEnabled;
public bool DhcpEnabled
{
get => _dhcpEnabled;
set
{
_dhcpEnabled = value;
OnPropertyChanged(nameof(DhcpEnabled));
OnPropertyChanged(nameof(EnableIpSetting));
}
}
public bool EnableIpSetting => _ipEnabled && !_dhcpEnabled;
Thank's in advance
What I've done so far:
My user control contains just some textboxes. I’ve got that DataContext = this; right after initializing my usercontroll.
Setting the IsEnabled static to true or false has the expected effect on these texboxes.
As soon as I try to bind it to the property it doesn’t work at all (even if Property is false at start texboxes aren’t disabled).
I set a breakpoint at the property which doesn’t hit.
I added this txtBxTest. To see if Binding works there. It does, breackpoint was hit.
Solved: My mistake was to change the datacontext (DataContext = this in the constructor of my usercontrol). That broke my binding to parent.
Anyway thank you all for supporting
You're binded to the "EnableIpSetting", but you are not calling OnPropertyChanged when it is updated.
You need to add your OnPropertyChanged logic to the EnableIpSetting as well.
I have made a User Control, FontSelector, that groups together a ComboBox for FontFamily Selection and three ToggleButtons for Bold, Italics, Underline options. I am having an issue with the ComboBox's SelectedItem property affecting all instances of that User Control within the same Window. For example, changing the ComboBox selection on one, will automatically change the other. For Clarity. I don't want this behavior. I am very surprised that a User Control is implicitly affecting another User Control.
XAML
<Grid x:Name="Grid" Background="White" DataContext="{Binding RelativeSource={RelativeSource AncestorType=local:FontSelector}}">
<ComboBox x:Name="comboBox" Width="135"
SelectedItem="{Binding Path=SelectedFontFamily}" Style="{StaticResource FontChooserComboBoxStyle}"
ItemsSource="{Binding Source={StaticResource SystemFontFamilies}}"/>
</Grid>
Code Behind
The CLR Property that the ComboBox's SelectedItem is Bound to. Code shown here is in the User Control Code Behind File, not a ViewModel.
private FontFamily _SelectedFontFamily;
public FontFamily SelectedFontFamily
{
get
{
return _SelectedFontFamily;
}
set
{
if (_SelectedFontFamily != value)
{
_SelectedFontFamily = value;
// Modify External Dependency Property Value.
if (value != SelectedFont.FontFamily)
{
SelectedFont = new Typeface(value, GetStyle(), GetWeight(), FontStretches.Normal);
}
// Notify.
RaisePropertyChanged(nameof(SelectedFontFamily));
}
}
}
The Dependency Property that updates it's value based on the Value of the ComboBox's SelectedItem Property. It effectively packages the FontFamily value into a Typeface Object.
public Typeface SelectedFont
{
get { return (Typeface)GetValue(SelectedFontProperty); }
set { SetValue(SelectedFontProperty, value); }
}
// Using a DependencyProperty as the backing store for SelectedFont. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SelectedFontProperty =
DependencyProperty.Register("SelectedFont", typeof(Typeface), typeof(FontSelector),
new FrameworkPropertyMetadata(new Typeface("Arial"), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnSelectedFontPropertyChanged)));
private static void OnSelectedFontPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = d as FontSelector;
var newFont = e.NewValue as Typeface;
if (newFont != null)
{
instance.SelectedFontFamily = newFont.FontFamily;
}
}
EDIT
I think I may have figured out what is going on. I can replicate it by Binding the ItemsSource to the Following Collection View Source.
<CollectionViewSource x:Key="SystemFontFamilies" Source="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Source"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
You can then replicate the behavior by placing 2 ComboBoxes and Binding both of them to the CollectionViewSource. They will now, seemingly implicitly track each others SelectedItem. Even without Any Data Binding outside of ItemsSource. It would seem that the CollectionViewSource is somehow playing a part in what the SelectedItem is.
I'd make it a bit different. I'll introduce this solution using only a String, not FontFamily or FontWeight, since I have no VS here right now. (In order to have it working, please change the list of FontFamilies to a list of strings to bind them.)
Your selector UserControl:
- your xaml is ok (but you won't need the x:Name)
- the CodeBehind of the UserControl (later: UC) should change, we will solve it with binding. You should have a DependencyProperty, lets' call it SelectedFontFamily, which will represent the selected string from the ComboBox:
public string SelectedFontFamily
{
get { return (string)GetValue(SelectedFontFamilyProperty); }
set { SetValue(SelectedFontFamilyProperty, value); }
}
public static readonly DependencyProperty SelectedFontFamilyProperty = DependencyProperty.Register("SelectedFontFamily", typeof(string), typeof(YourUC), new PropertyMetadata(string.Empty));
The Window, which contains the UC:
- You should include the namespace of the UC's folder in the opening tag of the window, eg:
<Window
...
xmlns:view="clr-namespace:YourProjectName.Views.UserControls">
- the window's DataContext should have a property with public set option (feel free to implement INotifyPropertyChange on it):
public string FontFamily {get; set;}
- in the Window's xaml you would use the UC this way:
<view:YourUC SelectedFontFamily="{Binding FontFamily, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
It's a two-way binding. You'll find the selected string as the value of the FontFamily property every time you change the SelectedItem.
Edit: you will need View Model class for the Window which is using the UserControl. Create it, make it implement the INotifyPropertyChanged interface, and set it as DataContext for your consumer window. WPF is not like WF, you can find more about it if you Google up "WPF MVVM" or something like that.
Found the problem. I was binding to a CollectionViewSource defined in Application Resources. Until now I was unaware that Binding to a CollectionViewSource will also affect the SelectedItem. The SelectedItem Data gets stored as part of the CollectionViewSource. Setting the IsSynchronizedWithCurrentItem property to False on the ComboBox solved the issue.
Here is an existing answer I have now Found.
Thanks
In my UserControl I have a Checkbox
<CheckBox DockPanel.Dock="Left" VerticalAlignment="Bottom" VerticalContentAlignment="Bottom" x:Name="showLegendsChk" Margin="10,0,0,0"
Content="View Legends" Checked="showLegendsChk_Checked" />
<!--IsChecked="{Binding ElementName=CrossSecViewWnd, Path=ShowLegends, Mode=TwoWay}" -->
I tried to add data binding to it, & added some logic on checked & non-checked; so no need to add event to the same.
private bool showLegendsWnd;
public CrossSectionalViewControl() {
FillLegends();
ShowLegends = false;
}
// Using a DependencyProperty as the backing store for
//IsCheckBoxChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowLegendsProperty =
DependencyProperty.Register("ShowLegends", typeof(bool),
typeof(CrossSectionalViewControl), new UIPropertyMetadata(false));
public bool ShowLegends
{
get { return showLegendsWnd; }
set
{
showLegendsWnd = value;
NotifyPropertyChanged("ShowLegends");
if (showLegendsWnd == true)
legendWrap.Visibility = System.Windows.Visibility.Visible;
else
legendWrap.Visibility = System.Windows.Visibility.Hidden;
Console.WriteLine("Show Legends = " + showLegendsWnd + " Chk Value = " + showLegendsChk.IsChecked);
}
}
Tried alot wit binding, but no success. Finally added checked event & commented binding property. -
private void showLegendsChk_Checked(object sender, RoutedEventArgs e)
{
showLegendsWnd = (bool)showLegendsChk.IsChecked;
Console.WriteLine("CHK Show Legends = " + showLegendsWnd + " Chk Value = " + showLegendsChk.IsChecked);
if (showLegendsWnd == true)
legendWrap.Visibility = System.Windows.Visibility.Visible;
else
legendWrap.Visibility = System.Windows.Visibility.Hidden;
legendWrap.UpdateLayout();
}
With this also, even when checkbox is unChecked, it doesn't fire event nor property with both checked & unchecked.
In both - Binding & Event 1 state event is fired properly but the other isn't !!! Have also added TwoWay mode, tried with UpdateSourceTrigger in binding but no success.
Why is this Strange problem with Checkbox....
For your event:
You also need to subscribe to the Unchecked event.
Change your xaml to:
<CheckBox x:Name="showLegendsChk"
DockPanel.Dock="Left"
VerticalAlignment="Bottom"
VerticalContentAlignment="Bottom"
Margin="10,0,0,0"
Content="View Legends"
Checked="showLegendsChk_Checked"
Unchecked="showLegendsChk_Checked" />
Now both events will fire the same handler and if you set a breakpoint in your handler, you can see it get called.
For your binding
Not quite sure what you're trying with it. Firstly the property you define for your DP's are merely a convenience for you and the underlying framework does not invoke it's setter's or getter's when updating the DP value. Next up, not sure why you're calling a NotifyPropertyChanged("ShowLegends"); on a DP. If my assumtion is right and that actually corresponds to a INPC raise property changed implementation, it's not needed for DP's
Start with trying simple stuff. Something like:
<CheckBox x:Name="chkBox"
IsChecked="{Binding IsChecked}" />
and in your DataContext class have the corresponding property
private bool _isChecked;
public bool IsChecked {
get { return _isChecked; }
set {
_isChecked = value;
Debug.WriteLine(string.Format("Checkbox check state changed to: {0}", _isChecked ? "Checked" : "Not Checked"));
}
}
You should see the Debug.WriteLine get invoked on changes to the IsChecked property. once you got to that stage add the rest of your logic step by step and validate if it still works and if it doesn't, it's prolly something you're adding causing it than a behavior of the system.
Update:
Download Link to sample
Attached sample should show three approaches. Event based, Simple Binding, Complex Binding connected to a DP from a custom control to then toggle that control's visibility.
I'm having yet another WPF binding issue. Just when I think I've got this stuff figured out, I run into more problems... :S
Anyway... I've created a custom user control for selecting files. It's a simple textbox followed by a button contained within a grid. The property of the control with which I am working is called FilePath and the TextBox on this control is bound to that property. When the button is clicked, a SaveFileDialog is opened and the user selects a file. The UI correctly updates after the user selects the file.
The problem I seem to be having is that when I bind an object to the control (in this instance I have an object with a DocumentFilePath property) the object doesn't update when a new file is selected.
Here's the relevant code within my user control:
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");
}
And the user control is added into my Window programatically by using reflection on my object:
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
}
It may be worth noting that if I load the window with an existing object, my user control displays properly but still won't register any changes within the object to which it is bound.
Please let me know if you guys need any more info.
Thanks in advance,
Sonny
EDIT: I've found a way around the problem, but this probably isn't a good solution. By watching the debugger carefully I found that when I set the FilePath property within my control, the object was being unbound. If anyone can shed some light on that, I would be most appreciative. In the mean time, I've changed the code that opens my SaveFileDialog to look like this:
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;
}
}
You're not specifying anywhere in your code that the FilePath property should be TwoWay so updates of the DP value won't get pushed to the bound source object's property. You can use either:
Binding b = new Binding(pi.Name){ Mode = BindingMode.TwoWay };
or you can set up your Dependency Property to use a default of TwoWay:
public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register(
"FilePath", typeof(string), typeof(FileSave),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnFilePathChanged));
You should also follow Robert's suggestion of removing the manual PropertyChange event, and also don't EVER add any code other than GetValue and SetValue in your DP wrapper property. XAML calls GetValue and SetValue directly so will skip over anything else you add there - which can lead to very nasty bugs.
Why, yes! I most certainly can shed some light on that!
Also, if you're using .Net 4.0, today's your lucky day!
Consider the following fine method on your DependencyObject:
SetCurrentValue();
Yes! With this SINGULAR method, all your woes will drift away as a bad dream at the rooster's crow! (Well, ok, not really, but that is the method you're looking for.)
Short story very short: When you programmatically SetValue() on a control in your view layer, you blow away your bindings. SetCurrentValue() was added to the framework because you frequently want to drive a change in your bound object by setting that value directly. An alternate design would be to set the value in your bound object programmatically and let the updated value get pulled back into the view, but that's frequently clumsy.
(I strongly suspect that the absence of this method up to this point is largely responsible for the utter failure of the vast majority of NumericUpDown controls in WPF.)
First, you don't need to raise the PropertyChanged event when a dependency property changes; with dependency properties, change notification comes for free.
What's probably happening here: The default behavior for UpdateSourceTrigger is LostFocus, i.e. the source gets updated when the user presses TAB to move to the next field, or clicks on another control, or whatever. The text box isn't losing focus after your SaveFileDialog sets Text (since it probably doesn't even have the focus in the first place), so the source update never gets triggered.
To make it update the source whenever the Text property changes, set the UpdateSourceTrigger to PropertyChanged.
If that doesn't work, watch the Output window for binding errors.
Edit:
Here's a little prototype application I built. It works just fine: typing in the text box sets the property, clicking on the "Save" button sets the property, and the binding in the main window gets updated properly no matter what.
<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;
}