It is simple thing that works everywhere instead of TextBox.Text without any reasons. What I need is just to get updated Text property of textbox when DataContext changed. There is a DataGrid and another control behind it to show details about row. So when user click's on row I am obtain data object from grid's row and show its details in this details control. Details control contains just propertyName and value control. I.e. textblock and textbox. ANY property of textblock or textbox are updated on changing DataContext except Text in textbox. I've break my head already.
The main problem is textbox won't updated after I've changed it. And it works fine if move textbox outside control to the details control (parent)
The property-Value control:
<DockPanel x:Class="UserControls.PropertySectionControl"
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"
mc:Ignorable="d"
x:Name="_propertyDockPanel"
HorizontalAlignment="Stretch" Margin="0 5 0 0"
d:DesignHeight="300" d:DesignWidth="300">
<TextBlock Text="{Binding Path=Property, ElementName=_propertyDockPanel}" TextWrapping="Wrap"/>
<TextBox
Text="{Binding Path=Value, ElementName=_propertyDockPanel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="{Binding Path=IsReadOnly, ElementName=_propertyDockPanel}"
TextAlignment="Right" Foreground="Gray" TextWrapping="Wrap" Width="120" Height="20" HorizontalAlignment="Right" Margin="0 0 15 0"
ToolTip="{Binding Value, ElementName=_propertyDockPanel}" Template="{StaticResource _inspectorValueTemplate}"
LostFocus="_textBoxValue_LostFocus" GotFocus="_textBoxValue_LostFocus" KeyDown="_textBoxValue_KeyDown" TextChanged="_textBoxValue_TextChanged"
/>
</DockPanel>
And here is how it is used in details control:
<userControls:PropertySectionControl Property="Total" Value="{Binding OrderCharges.Total}" IsReadOnly="True"/>
As you can see, in PropertySectionControl there is also binding ToolTip to Value which is works on DataContext changed! But for TextBox.Text is not. What is this?
UPD:
PropertySectionControl.cs
public partial class PropertySectionControl : DockPanel
{
public string Property
{
get { return (string)GetValue(PropertyProperty); }
set { SetValue(PropertyProperty, value); }
}
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(PropertySectionControl), new PropertyMetadata(""));
public static readonly DependencyProperty PropertyProperty =
DependencyProperty.Register("Property", typeof(string), typeof(PropertySectionControl), new PropertyMetadata(""));
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(PropertySectionControl), new PropertyMetadata(false));
public PropertySectionControl()
{
InitializeComponent();
}
/// <summary>
/// Static event handler is for use in Inspector control as well
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public static void TextBox_LostOrGotFocus(object sender, RoutedEventArgs e)
{
var textBox = sender as TextBox;
if (textBox.IsFocused)
{
textBox.TextAlignment = TextAlignment.Left;
textBox.SelectAll();
}
else
{
textBox.TextAlignment = TextAlignment.Right;
}
}
public static void TextBox_KeyDown(object sender, KeyEventArgs e)
{
var textBox = sender as TextBox;
if (e.Key == Key.Enter)
{
textBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
else if (e.Key == Key.Escape)
{
int i = 0;
do
{
textBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
} while (!(Keyboard.FocusedElement is TextBoxBase) && ++i < 5); // prevent infinite loop
Keyboard.ClearFocus();
}
}
public static void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var textBox = sender as TextBox;
textBox.Height = 20;
for (int i = 2; i <= textBox.LineCount; i++)
textBox.Height += 14;
}
private void _textBoxValue_LostFocus(object sender, RoutedEventArgs e)
{
TextBox_LostOrGotFocus(sender, e);
}
private void _textBoxValue_KeyDown(object sender, KeyEventArgs e)
{
TextBox_KeyDown(sender, e);
}
private void _textBoxValue_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox_TextChanged(sender, e);
}
}
Just guessing:
1) That textbox has a textchanged eventhandler set. Check if that handler works OK. Try to comment it out ans see if it changes anything. I.e. maybe it gets fired during the data update from model and maybe it throws an exception and binding is aborted in the meantime?
1a) run in VS in Debug mode and check 'Output' window. See if there is anything reported like:
first chance exceptions
binding errors
important especially if they show up when you try editing the textbox for the first time
2) Also, whenever a binding magically stops working, be sure to check if the binding is still in place. I see you are using a direct access to the controls from code-behind (like textbox.Height += ..). This is an easy way to break the bindings. If you ever anywhere ran one of these lines:
textBox.Text = ""
textBox.Text = "foo"
textBox.Text = john.name
textBox.Text += "."
these may have a high chance of unsetting your bindings on Text on that textbox. I don't see any such line in the code you provided, but maybe you have it elsewhere.
You can easily check if the binding is still inact by running:
object realvalue = textBox.ReadLocalValue(TextBox.TextProperty);
now if the realvalue is null, or string, or anything other than a Binding object - that means that something accessed the textbox and replaced the binding with a concrete constant value, and you need to find and correct that so that .Text is not assigned and instead of that the Value property of source object (customdockpanel) is changed.
Related
When loading :
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
radioButtonWatchDirectory.IsChecked = Properties.Settings.Default.RadioButtonWatchDirectory;
radioButtonWatchFile.IsChecked = Properties.Settings.Default.RadioButtonWatchFile;
checkBoxIncludeSubdirectories.IsChecked = Properties.Settings.Default.IncludeSubDirectories;
textBoxFileDirectory.Text = Properties.Settings.Default.BrowseFolderDialog;
}
When setting and saving :
private void radioButtonWatchFile_Checked(object sender, RoutedEventArgs e)
{
Properties.Settings.Default.RadioButtonWatchFile = (bool)radioButtonWatchFile.IsChecked;
Properties.Settings.Default.Save();
}
private void radioButtonWatchDirectory_Checked(object sender, RoutedEventArgs e)
{
Properties.Settings.Default.RadioButtonWatchDirectory = (bool)radioButtonWatchDirectory.IsChecked;
Properties.Settings.Default.Save();
}
When running the application once i checked true the Watch File radio button no matter if i check the Watch Directory radio button next time i will run the application the Watch File radio button will be checked. like it's not remembering changing it to the Watch Directory radio button.
The Include Subdirectories is working fine.
I tried this when loading :
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
radioButtonWatchDirectory.IsChecked = Properties.Settings.Default.RadiosTesting;
radioButtonWatchFile.IsChecked = Properties.Settings.Default.RadiosTesting;
checkBoxIncludeSubdirectories.IsChecked = Properties.Settings.Default.IncludeSubDirectories;
textBoxFileDirectory.Text = Properties.Settings.Default.BrowseFolderDialog;
}
When saving :
private void RadiosTests(object sender, RoutedEventArgs e)
{
if((bool)radioButtonWatchFile.IsChecked)
{
Properties.Settings.Default.RadiosTesting = (bool)radioButtonWatchFile.IsChecked;
Properties.Settings.Default.Save();
}
if((bool)radioButtonWatchDirectory.IsChecked)
{
Properties.Settings.Default.RadiosTesting = (bool)radioButtonWatchDirectory.IsChecked;
Properties.Settings.Default.Save();
}
}
but always the radioButtonWatchFile is checked when running the application again.
When a radio button is unchecked, the Checked event is not fired again. So, when you select another option, the setting of the previously checked option is not changed.
You could additionally subscribe to the Unchecked event of each radiobutton in order to manage the state in your settings object.
However, it might be a better idea to save some consolidated state of the currently selected option, instead of saving each radiobutton state individually.
Answer to why it always selects the file option: because both IsChecked properties are assigned true but only one can legally be selected, the selection of the first radiobutton is reset when the second one is initialized.
If you would revert the order of loading, the other button would be selected (this code is not a solution, just a demonstration of your error the other way around)
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// reverse initialization order changes the outcome
radioButtonWatchFile.IsChecked = Properties.Settings.Default.RadiosTesting;
radioButtonWatchDirectory.IsChecked = Properties.Settings.Default.RadiosTesting;
// ...
}
€dit: example of managing the radiobutton state by name with a somewhat mvvm like approach.
Suppose you create a viewmodel for your mode selection:
// consider linking this viewmodel to your settings somehow
class ModeViewModel
{
public string SelectedMode { get; set; }
public bool WithSubdirectories { get; set; }
}
In your groupbox, listen to Checked events and establish a property to maintain your selected RadioButton state => Tag in this example. The checked event handler will be responsible for setting the Tag depending on the actual RadioButton selection. When a new selected RadioButton is assigned, the ModeViewModel is updated accordingly.
<GroupBox Name="ModeGroup" Header="Mode"
ToggleButton.Checked="RadioButtonChecked"
Tag="{Binding SelectedMode, Mode=OneWayToSource}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<RadioButton Name="WatchFile" Content="Watch File"/>
<RadioButton Name="WatchFolder" Content="Watch Directory"
Grid.Row="1" Grid.Column="0" HorizontalAlignment="Left"/>
<CheckBox Content="Include Subdirectories" IsChecked="{Binding WithSubdirectories}"
Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right"/>
</Grid>
</GroupBox>
Checked event handler that maintains the Tag state depending on the selected RadioButton.Name
private void RadioButtonChecked(object sender, RoutedEventArgs e)
{
if (e.Source is RadioButton rb && sender is GroupBox gb)
gb.Tag = rb.Name;
}
Load the selected radiobutton state from a string, containing the name of a RadioButton.
// Initialize the datacontext with your ModeViewModel
public MainWindow()
{
InitializeComponent();
ModeGroup.DataContext = new ModeViewModel();
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// Load from your settings instead
var selectedMode = nameof(WatchFolder);
if (!string.IsNullOrEmpty(selectedMode) &&
LogicalTreeHelper.FindLogicalNode(ModeGroup, selectedMode) is RadioButton checkedElement)
{
checkedElement.IsChecked = true;
}
}
If you tweak this example and actually use your settings as initialization source and backing store of your viewmodel, this should work for your scenario.
Working solution.
void radioButton_Checked(object sender, RoutedEventArgs e)
{
if ((bool)radioButtonWatchFile.IsChecked)
{
Properties.Settings.Default.RadioButtonWatchFile = true;
Properties.Settings.Default.RadioButtonWatchDirectory = false;
}
else
{
Properties.Settings.Default.RadioButtonWatchFile = false;
Properties.Settings.Default.RadioButtonWatchDirectory = true;
}
Properties.Settings.Default.Save();
}
I opened the question here but we cannot come to the solution for my problem. I decided to create new question as we came to some assumptions and the former question does not refer to the real problem(we thought it is the problem with binding but as you will read on it is not).
In few words I have a ListView with data from list called jointList.
The list is doing well and it has all the data necessary. (I checked it)
On each row of the ListView I put a ToggleSwitch(in xaml) and then I try to do something with each of the switches.
Each switch should correspond to the data from the same row.
I created Toggled event that should apply to all toggleSwitches like this:
private void ToggleSwitch_Toggled(object sender, RoutedEventArgs e)
{
foreach (var product in jointList)
{
if (product.IsOn == true)
{
ToggleTest.Text = product.ProductId.ToString(); // this is for testing only, later I would do something with the data retrieved
ToggleTest.Visibility = Visibility.Visible;
}
else
{
ToggleTest.Visibility = Visibility.Collapsed;
}
}
}
But this is making only one toggleSwitch work. It's the switch that corresponds to the last added product to the list ( I am guessing that it is refering to the last Id). The other switches return nothing as if the method was not iterating through the list correctly or as if there was only one switch hooked up.
So, is it possible to get all switches up and running by using just one Toggled event as I attempt to do?
Here's a sample which shows one way.
In this example we have the following Product view model:
public class Product : INotifyPropertyChanged
{
private string _name;
public string Name
{
get => _name;
set
{
if (value == _name) return;
_name = value;
OnPropertyChanged();
}
}
So just a single Name-property.
Then we have MainPage where we create a collection of products:
private void FrameworkElement_OnLoaded(object sender, RoutedEventArgs e)
{
var items = new ObservableCollection<Product>();
for (int i = 0; i < 9; i++)
{
items.Add(new Product($"item {i}"));
}
this.Items.ItemsSource = items;
}
And the XAML which creates the view:
<ListView Loaded="FrameworkElement_OnLoaded" x:Name="Items">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="RowContent" Text="{Binding Name}"/>
<ToggleSwitch x:Name="Toggle" Grid.Column="1" Toggled="Toggle_OnToggled"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The result:
Now we want to change the text when user toggles the switch. This is done in Toggle_OnToggled-event handler:
private void Toggle_OnToggled(object sender, RoutedEventArgs e)
{
var toggle = (ToggleSwitch) sender;
var dataContext = ((Grid)toggle.Parent).DataContext;
var dataItem = (Product) dataContext;
dataItem.Name = $"Toggled {toggle.IsOn}";
}
So after a few toggles:
Mikael Koskinen has delivered the answer to my problem.
Most of my code was correct and identical to his solution, apart from the last bit that is OnToggled event handler.
Here is the working andd correct handler:
private void Toggle_OnToggled(object sender, RoutedEventArgs e)
{
var toggle = (ToggleSwitch)sender;
var dataContext = ((Grid)toggle.Parent).DataContext;
var dataItem = (ScheduleList)dataContext;
ToggleTest.Text = dataItem.ProductId;
}
My previous version of handler didn't include the important bit, that is dataContext and dataItem.
It works like a charm now.
Evening all, I've run into an issue with the SelectionChanged (TabControl) event being call before the LostFocus (TextBox) event.
This is a problem since the SelectionChanged is triggered during a tab change, that intern resets the ListView.SelectedIndex (TabControl>TabItem>ListView) to -1.
The textbox uses LostFocus to update/validate it's textbox.text which depend upon the SelectedIndex. The text in the textbox is stored/retrieved from a List<string> and because the index changed, the List happens to go out of bounds.
I've looked around, tired a few things also a "hack-y" approach which didn't really help.
<TabControl SelectionChanged="SelectionChanged_Tab"
<TabItem .../>
<TabItem .../>
</TabControl>
<Grid>
<Label .../>
<TextBox Name="Name" LostFocus="Lost_Focus" .../>
</Grid>
Code:
private void SelectionChanged_Tab(object sender, SelectionChangedEventArgs e)
{
if (e.Source is TabControl)
{
ListView1.SelectedIndex = -1;
ListView2.SelectedIndex = -1;
}
}
private void Lost_Focus(object sender, RoutedEventArgs e)
{
TextBox textbox = sender as TextBox;
int Index = ListView.SelectedIndex;
if (string.IsNullOrEmpty(textbox.Text) || textbox.Text == "0")
{
textbox.Text = "0";
}
switch (textbox.Name)
{
case "Name":
SomeList[Index].AProperty = textbox.Text;
break;
}
}
OK, so after think about the problem from a different perspective, I decided to simple make the TabControl, an Focus-able event and simple make it focus when selection changes:
<TabControl SelectionChanged="SelectionChanged_Tab" Focusable="True"
<TabItem .../>
<TabItem .../>
</TabControl>
Code:
private void SelectionChanged_Tab(object sender, SelectionChangedEventArgs e)
{
if (e.Source is TabControl)
{
ListView2.Focus();
ListView1.SelectedIndex = -1;
ListView2.SelectedIndex = -1;
}
if (ListView2.SelectedIndex == -1)
{
ListView1.Focus();
}
}
I know it's not the most elegant solution (In the process or re-factoring) but this seems to get the job done.
I'm stuck on a strange problem.
I have an AutoCompleteBox in my view
<sdk:AutoCompleteBox x:Name="txtSIA"
Grid.ColumnSpan="1" Grid.Row="1" Grid.Column="1"
SelectedItem="{Binding SIA, Mode=TwoWay, ValidatesOnNotifyDataErrors=True}"
Text="{Binding TextSIA, Mode=TwoWay}"
KeyUp="TxtSIA_KeyUp"
Populating="SIANonSIU_Populating"
Style="{StaticResource AutoCompleteStyle}"
/>
I implemented a field validator that check if its text isn't null or an empty string.
It works pretty well but the tricky part is that I have a button that Reset all my controls values, which code from my viewmodel is:
void BtnReset_OnClick(RoutedEventArgs e)
{
SIA = new SIA();
TextSIA = string.Empty;
BtnGeneralIsEnabled = false;
DataGridSource = null;
}
Whenever I click it and then write in my AutoCompleteBox, the AutoCompleteBox is never empty or null even in my code behind in a key up event listener.
Here are some picture to illustrate my point:
Register to the TextChanged event of the AutoCompleteBox instead of the KeyUp:
"Occurs when the text in the text box portion of the AutoCompleteBox changes."
I found an answer here
To fix that but we have to create a new autocompletebox and override the OnApplyTemplate method.
public class CustomAutoComplete : AutoCompleteBox
{
TextBox mytext;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
mytext = GetTemplateChild("Text") as TextBox;
mytext.TextChanged += new System.Windows.Controls.TextChangedEventHandler(mytext_TextChanged);
}
void mytext_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
this.Text = mytext.Text;
OnTextChanged(new RoutedEventArgs());
}
}
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;
}