2011-12-19 4 views
4

私はカスタムコントロールを作成しました。これは、OpenFileDialogを開くボタン付きのテキストボックスです。CustomControl DependencyPropertyバインディングが正しく機能しない

TextBoxのTextプロパティは、私の依存関係プロパティ "FileName"にバインドされています。ユーザーがOpenFileDialogを介してファイルを選択すると、結果がこのプロパティに設定されます。

TextBoxは、バインドによって正しい値を取得します。

しかし、今私の問題。私の見解では私はViewModelを使用しています。だから、私は自分のDependencyProperty "FileName"を私のViewModelのプロパティにバインドしています。 "FileName"プロパティ(テキストボックスに直接変更するか、ダイアログボックスでファイルを選択する)を変更すると、viewmodelプロパティは更新されません。ビューで使用

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> 

<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> 

との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 
    } 
} 

依存関係プロパティの使用に問題はありますか? 注:問題はDataGridでのみ発生します。

+0

多分それは私が使用している場合、この問題にのみ発生すると言うことが重要ですwpfツールキットのDataGridTemplateColumnのコントロール。 –

+0

問題は、ObservableCollectionが新しい要素が追加または削除された場合にだけリスト要素の変更を認識しないことでした。ソリューションは、ここでstackoverflowで見つけることができるVeryObservableCollectionの概念です。 –

答えて

15

デフォルトのバインディングは一方向、つまりビューモデルからの変更の読み込みで、更新はされないため、ModeからTwoWayに設定する必要があります。

<controls:FileSelectorTextBox FileName="{Binding FileName, Mode=TwoWay}" Height="30" /> 

別のオプションのように、BindsTwoWayByDefaultフラグを付けてカスタム依存関係プロパティを宣言することです。また、

public static readonly DependencyProperty FileNameProperty = 
      DependencyProperty.Register("FileName", 
             typeof(string), 
             typeof(FileSelectorTextBox), 
             new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 

あなたがコントロール使用内部SetCurrentValueメソッドからカスタム依存関係プロパティを変更する代わりに直接割り当てます値はプロパティセッターを使用します。あなたがそれを直接割り当てると、あなたはバインディングを壊すでしょう。だから、

、代わりに:

FileName = dlg.FileName; 

は次のようでください:

SetCurrentValue(FileNameProperty, dlg.FileName); 
+0

ああ、元の投稿に間違ったコードを残して申し訳ありません。デフォルトのバインディングモードを設定しました。フレームワークのプロパティマップによって2Wayが設定されています –

+0

@FelixCzylwik - (コントロール内から依存関係プロパティの値を設定する方法について) 。 –

+0

Mh、それはまさに私の問題ですが、SetCurrentValueが.NET 3.5に存在しないようです。 –

1

次のように変更します。

<TextBox Name="txtFileName" 
        HorizontalAlignment="Stretch" 
        VerticalAlignment="Center" 
        Grid.Column="0" 
        Text="{Binding FileName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 
+1

'TextBox'の' Text'プロパティへのバインディングはデフォルトで 'TwoWay'です。 –

関連する問題