2010-12-20 30 views
4

もう1つのWPFバインディングの問題があります。ちょうど私がこのことを理解したと思うと、私はもっと多くの問題に遭遇します...:SWPFバインディングの問題 - UI更新、オブジェクトがありません

とにかく...私はファイルを選択するためのカスタムユーザーコントロールを作成しました。これは単純なテキストボックスで、その後にグリッド内のボタンが表示されます。私が動作しているコントロールのプロパティはFilePathと呼ばれ、このコントロールのTextBoxはそのプロパティにバインドされています。ボタンをクリックすると、SaveFileDialogが開き、ユーザーがファイルを選択します。ユーザーがファイルを選択すると、UIが正しく更新されます。

問題は、オブジェクトをコントロールにバインドすると(この例ではDocumentFilePathプロパティを持つオブジェクトがある)、新しいファイルが選択されたときにオブジェクトが更新されないということです。

ここに私のユーザーコントロール内の関連コードです:

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"); 
} 

そしてユーザーコントロールが私のオブジェクトにリフレクションを使用してプログラム的に私のウィンドウに追加されます。

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 
} 

それは注目に値するかもしれ既存のオブジェクトでウィンドウをロードすると、ユーザコントロールは正しく表示されますが、バインドされているオブジェクト内の変更はまだ登録されません。

あなたに必要な情報があれば教えてください。事前に

おかげで、
ソニー

EDIT:私はこの問題を回避する方法を見つけたが、これはおそらく、良い解決策ではありません。デバッガを注意深く見て、コントロール内でFilePathプロパティを設定すると、オブジェクトがアンバインドされていることがわかりました。もし誰かがそれを少しでも見せることができれば、私は最も感謝しています。その間、私はこのように見て、私のSaveFileDialogを開くコードを変更しました:

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

答えて

3

あなたはあなたのコード内の任意の場所に指定していないことFilePathプロパティはTwoWayである必要があります。したがって、DP値の更新はバインドされたソースオブジェクトのプロパティにプッシュされません。あなたはどちらかを使用することができます:

Binding b = new Binding(pi.Name){ Mode = BindingMode.TwoWay }; 

か、双方向のデフォルトを使用するように依存関係プロパティを設定することができます。

public static readonly DependencyProperty FilePathProperty = DependencyProperty.Register(
"FilePath", typeof(string), typeof(FileSave), 
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnFilePathChanged)); 

あなたはまた、手動PropertyChangeイベントを除去するロバートの提案に従ってください、そしてまたドンなければなりませんDPラッパープロパティにGetValueおよびSetValue以外のコードを追加したことはありません。 XAMLはGetValueとSetValueを直接呼び出すため、そこに追加する他のものはスキップします。非常に厄介なバグにつながる可能性があります。

+0

ありがとうございます。それがそれでした。 –

1

まず、あなたがPropertyChangedイベントを発生させる必要がない場合、依存関係プロパティが変更され、依存関係のプロパティでは、変更通知が無料で提供されます。

UpdateSourceTriggerのデフォルトの動作はLostFocusです。つまり、ユーザーがTabキーを押して次のフィールドに移動するか、別のコントロールをクリックしたときなどにソースが更新されます。 SaveFileDialogTextに設定された後にテキストボックスにフォーカスが失われていないため、ソース更新が実行されることはありません。

Textプロパティが変更されるたびにソースを更新するには、UpdateSourceTriggerPropertyChangedに設定します。

出力されない場合は、出力ウィンドウでバインディングエラーを確認してください。

編集:

ここで私が構築された小さなプロトタイプアプリケーションです。テキストボックスに入力するとプロパティが設定され、「保存」ボタンをクリックするとプロパティが設定され、メインウィンドウのバインディングは何に関係なく正しく更新されます。

<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; 
} 
+0

ありがとう、ロバート。どちらのオプションでも運がない。私は回避策を見つけましたが、何がうまくいかないのかについて何らかの光を当てることができますか?元の投稿の私の編集を見てください。 –

+0

Robert、あなたの編集に関して、ICommandサーバーはどのような目的ですか?私のUserControlは持っていません。 –

2

なぜなら、はい!私は確かにそれについていくつかの光を当てることができます!

また、.Net 4.0を使用している場合、今日はあなたの幸運な日です!はい

SetCurrentValue();

はあなたのDependencyObject上で以下の罰金の方法を考えてみましょう!この単一の方法では、あなたのすべての悲しみは、雄鶏のカラスで悪夢として漂うでしょう!

ショート・ストーリー非常に短い:ビュー・レイヤー内のコントロールでプログラムでSetValue()をコントロールすると、バインディングが吹き飛ばされます。 SetCurrentValue()はフレームワークに追加されました。その値を直接設定することで、バインドされたオブジェクトの変更を頻繁に実行したいからです。別の設計では、バウンドオブジェクトの値をプログラムで設定し、更新された値をビューに戻すことができますが、それはしばしば不器用です。

は(私は強く、この時点まで、この方法までが存在しない場合は、WPFでのNumericUpDownをコントロールの大半の全くの失敗の大きな原因であると思われる。)

+0

私は自分のコードで何をやっているのですか?私の 'FilePickerSaveCommand'のコードがバインディングを吹き飛ばすのはなぜですか? –

+0

さて、私のバインディングは維持されますが、バインディングオブジェクトはまだ新しい値で更新されていません。 –

関連する問題