CustomControl DependencyProperty de Liaison ne fonctionne pas correcte

J'ai écrit un customcontrol. C'est une zone de texte avec un bouton qui ouvre un OpenFileDialog.

La propriété Text de la zone de texte est lié à ma dépendance à la propriété "nom de fichier". Et si l'utilisateur sélectionne un fichier via l'OpenFileDialog, j'ai mis le résultat de cette propriété.

La zone de texte devient la juste valeur par le biais de la liaison.

Mais maintenant mon problème. De mon point de vue, je suis en utilisant un ViewModel. J'ai donc une Liaison à mon DependencyProperty "nom de fichier" à la propriété dans mon ViewModel.
Après un changement de "nom de fichier" de la propriété (les modifications directement dans la zone de texte ou de sélectionner un fichier via la boîte de dialogue), le viewmodel de la propriété n'est pas mise à jour.

CustomControl.xaml.cs

using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
namespace WpfApplication1.CustomControl
{
///<summary>
///Interaction logic for FileSelectorTextBox.xaml
///</summary>
public partial class FileSelectorTextBox
: UserControl, INotifyPropertyChanged
{
public FileSelectorTextBox()
{
InitializeComponent();
DataContext = this;
}
#region FileName dependency property
public static readonly DependencyProperty FileNameProperty = DependencyProperty.Register(
"FileName",
typeof(string),
typeof(FileSelectorTextBox),
new FrameworkPropertyMetadata(string.Empty,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnFileNamePropertyChanged),
new CoerceValueCallback(OnCoerceFileNameProperty)));
public string FileName
{
get { return (string)GetValue(FileNameProperty); }
set { /*SetValue(FileNameProperty, value);*/ CoerceFileName(value); }
}
private bool _shouldCoerceFileName;
private string _coercedFileName;
private object _lastBaseValueFromCoercionCallback;
private object _lastOldValueFromPropertyChangedCallback;
private object _lastNewValueFromPropertyChangedCallback;
private object _fileNameLocalValue;
private ValueSource _fileNameValueSource;
private static void OnFileNamePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is FileSelectorTextBox)
{
(d as FileSelectorTextBox).OnFileNamePropertyChanged(e);
}
}
private void OnFileNamePropertyChanged(DependencyPropertyChangedEventArgs e)
{
LastNewValueFromPropertyChangedCallback = e.NewValue;
LastOldValueFromPropertyChangedCallback = e.OldValue;
FileNameValueSource = DependencyPropertyHelper.GetValueSource(this, FileNameProperty);
FileNameLocalValue = this.ReadLocalValue(FileNameProperty);
}
private static object OnCoerceFileNameProperty(DependencyObject d, object baseValue)
{
if (d is FileSelectorTextBox)
{
return (d as FileSelectorTextBox).OnCoerceFileNameProperty(baseValue);
}
else
{
return baseValue;
}
}
private object OnCoerceFileNameProperty(object baseValue)
{
LastBaseValueFromCoercionCallback = baseValue;
return _shouldCoerceFileName ? _coercedFileName : baseValue;
}
internal void CoerceFileName(string fileName)
{
_shouldCoerceFileName = true;
_coercedFileName = fileName;
CoerceValue(FileNameProperty);
_shouldCoerceFileName = false;
}
#endregion FileName dependency property
#region Public Properties
public ValueSource FileNameValueSource
{
get { return _fileNameValueSource; }
private set
{
_fileNameValueSource = value;
OnPropertyChanged("FileNameValueSource");
}
}
public object FileNameLocalValue
{
get { return _fileNameLocalValue; }
set
{
_fileNameLocalValue = value;
OnPropertyChanged("FileNameLocalValue");
}
}
public object LastBaseValueFromCoercionCallback
{
get { return _lastBaseValueFromCoercionCallback; }
set
{
_lastBaseValueFromCoercionCallback = value;
OnPropertyChanged("LastBaseValueFromCoercionCallback");
}
}
public object LastNewValueFromPropertyChangedCallback
{
get { return _lastNewValueFromPropertyChangedCallback; }
set
{
_lastNewValueFromPropertyChangedCallback = value;
OnPropertyChanged("LastNewValueFromPropertyChangedCallback");
}
}
public object LastOldValueFromPropertyChangedCallback
{
get { return _lastOldValueFromPropertyChangedCallback; }
set
{
_lastOldValueFromPropertyChangedCallback = value;
OnPropertyChanged("LastOldValueFromPropertyChangedCallback");
}
}
#endregion FileName dependency property
private void btnBrowse_Click(object sender, RoutedEventArgs e)
{
FileDialog dlg = null;
dlg = new OpenFileDialog();
bool? result = dlg.ShowDialog();
if (result == true)
{
FileName = dlg.FileName;
}
txtFileName.Focus();
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion INotifyPropertyChanged
}
}

CustomControl.xaml

<UserControl x:Class="WpfApplication1.CustomControl.FileSelectorTextBox"
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"
d:DesignHeight="23" d:DesignWidth="300">
<Border BorderBrush="#FF919191"
BorderThickness="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="80" />
<ColumnDefinition Width="30" />
</Grid.ColumnDefinitions>
<TextBox Name="txtFileName"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Grid.Column="0"
Text="{Binding FileName}" />
<Button Name="btnBrowse"
Click="btnBrowse_Click"
HorizontalContentAlignment="Center"
ToolTip="Datei auswählen"
Margin="1,0,0,0"
Width="29"
Padding="1"
Grid.Column="1">
<Image Source="../Resources/viewmag.png"
Width="15"
Height="15" />
</Button>
</Grid>
</Border>
</UserControl>

L'aide d'un point de vue:

<Window x:Class="WpfApplication1.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication1.ViewModels"
xmlns:controls="clr-namespace:WpfApplication1.CustomControl"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="10" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Files}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="File name" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<controls:FileSelectorTextBox FileName="{Binding .}" Height="30" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<ListBox ItemsSource="{Binding Files}" Grid.Row="2">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>

Et le ViewModel:

using System.Collections.ObjectModel;
using System.ComponentModel;
namespace WpfApplication1.ViewModels
{
internal class MainViewModel
: INotifyPropertyChanged
{
public MainViewModel()
{
Files = new ObservableCollection<string> { "test1.txt", "test2.txt", "test3.txt", "test4.txt" };
}
#region Properties
private ObservableCollection<string> _files;
public ObservableCollection<string> Files
{
get { return _files; }
set
{
_files = value;
OnPropertyChanged("Files");
}
}
#endregion Properties
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion INotifyPropertyChanged Members
}
}

Est-il de toute mauvaise utilisation de la propriété de dépendance?
Remarque: ce problème se produit uniquement dans la grille de données.

C'est peut-être important de dire que ce problème ne se produit que si j'utilise le contrôle dans une DataGridTemplateColumn de wpf toolkit.
Le problème était que l'ObservableCollection ne pas tenir compte des changements apportés aux éléments de la liste, juste si de nouveaux éléments sont ajoutés ou supprimés. La solution est le concept de la VeryObservableCollection qui peut être trouvé ici sur stackoverflow.

OriginalL'auteur Felix C | 2011-12-19