2016-09-23 2 views
0

ソリューションの提案が多数確認されても、xamlで簡単にバインドすることはできません。私はWindow、dataContext、Appを持っています。問題は次のとおりです。WPF双方向バインディングがチェックボックスとリストで機能しない<string>

a)Appコンストラクタが実行されている間に、C#コードのチェックボックスの値を切り替えても、Window(同じコンストラクタで初期化され、 -ed)が表示されますが、カップル回;

b)Appコンストラクタが終了すると、Windowは正確に1回更新されます。私は、それが表示される文字列のリストのサイズを増やす必要がありますので、私は、ウィンドウ内のチェックボックスをクリックすると、アプリケーションのイベントハンドラ(DataContextプロパティの変更通知にバインドされている)を設定するように設定している。リストの増加はコード内で正しく発生しますが、ウィンドウには反映されません。

概要:ウィンドウ内

  • ユーザー入力細かいアプリケーションのC#コード達する:私は、チェックボックスの変更などに基づいて行動することができます
  • 反対方向には動作しません:アイテムが変更されるたびにコードを介してdataContext、iNotifyPropertyが実装されて実行されていても、Windowは自動的に更新されません。私が期待する何

ということです、ウィンドウボックスにチェックマークをクリアする/設定することにより、変更を反映する必要があり

A)のAppコンストラクタの実行中およびCheckBoxの値を切り替えます。

b)Appコンストラクタが終了した後、CheckBoxをFALSEからTRUEに切り替えるたびに、NameListに新しい文字列が追加されます。ウィンドウ内のリストがそれに応じて増加し、完全に添付されたNameListの内容が自動的に表示されると思います。

観察

  • 私はウィンドウ上のDataContextは、ウィンドウ上InitializeComponentを呼び出す前に設定されていることを確認してみてください。 CheckBoxのパスだけでなく、NameListバインディングリストボックスは、しかし Cannot resolve symbol due to unknown DataContext 、アプリケーションでアノテートされています。本当に
  • 私は、単一のMainWindow.xamlファイルでVSでの手掛かりを得る...残念ながら違いはありません。コンストラクタが終了し、ウィンドウが更新され、CheckBoxをクリックすると、正しいNotifyPropertyイベントがトリガされます。これは、実行時バインディングが実際に動作するはずであることを示しています...明らかに片方向で、双方向ではありません。

MainWindow.xaml:

<Window x:Class="StatisticsEvaluation.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525"> 
<Grid> 
    <StackPanel Orientation="Vertical"> 

     <CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" Content="CheckBox" /> 

     <ListBox ItemsSource="{Binding NameList, Mode=TwoWay}"> 
      <ListBox.ItemTemplate> 
       <DataTemplate> 
        <StackPanel> 
         <TextBlock Text="{Binding}"/> 
        </StackPanel> 
       </DataTemplate> 
      </ListBox.ItemTemplate> 
     </ListBox> 

     <TextBlock FontSize="18" FontFamily="Arial" Foreground="Black" Text="TextBlock" Visibility="Visible" /> 

    </StackPanel> 
</Grid> 

MainWindow.xaml。CS:

namespace StatisticsEvaluation 
{ 
/// <summary> 
/// Interaction logic for MainWindow.xaml 
/// </summary> 
public partial class MainWindow : Window 
{ 
    public MainWindow() 
    {    
    } 
} 

}

アプリケーションとのDataContext:

namespace StatisticsEvaluation 
{ 
    public class DataContextClass : INotifyPropertyChanged 
    { 
     private bool isChecked; 

     public bool IsChecked 
     { 
      get 
      { 
       return isChecked; 
      } 

      set 
      { 
       isChecked = value; 
       OnPropertyChanged("IsChecked"); 
      } 
     } 

     private List<string> nameList; 

     public List<string> NameList 
     { 
      get 
      { 
       return nameList; 
      } 

      set 
      { 
       nameList = value; 
       OnPropertyChanged("NameList"); 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     public void OnPropertyChanged(string propertyName) 
     { 
      var handler = PropertyChanged; 
      if (handler != null) 
       handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 


    /// <summary> 
    /// Interaction logic for App.xaml 
    /// </summary> 

    public partial class App : Application 
    { 
     private MainWindow MyWindow { get; set; } 

     private DataContextClass MyDataContext{ get; set; } 

     private void HandleDataContextPropertyChange(object sender, PropertyChangedEventArgs e) 
     { 
      // If the CheckBox was just toggled to TRUE, increase the NameList 
      // with an additional name and call OnPropertyChanged on it ... 
      // hoping that this would trigger a Window UI update - but no luck ! 

      if ((e.PropertyName == "IsChecked") && MyDataContext.IsChecked) 
      { 
       var randomProvider = new Random(); 
       MyDataContext.NameList.Add(randomProvider.Next().ToString()); 
       MyDataContext.OnPropertyChanged("NameList"); 
      } 
     } 

     public App() 
     { 
      MyDataContext = new DataContextClass(); 
      MyDataContext.PropertyChanged += HandleDataContextPropertyChange; 

      MyWindow = new MainWindow {DataContext = MyDataContext}; 
      MyWindow.InitializeComponent(); 
      MyWindow.Show(); 

      MyDataContext.NameList = new List<string>(); 
      MyDataContext.NameList.Add("FirstName"); 
      MyDataContext.NameList.Add("SecondName"); 
      MyDataContext.NameList.Add("ThirdName"); 

      MyDataContext.IsChecked = true; 
      Thread.Sleep(3000); 
      MyDataContext.IsChecked = false; 
      Thread.Sleep(3000); 
      MyDataContext.IsChecked = true; 
     }  
    } 
} 

私は、Appコンストラクタがヒットしたら、アプリケーションは次のようなウィンドウが表示され起動すると.Show

enter image description here

アプリのコンストラクタh仕上げとして、ウィンドウが一度更新されますが、二度とその後、関係なく、どのように多くの文字列がNameListに追加されます。

enter image description here

任意のアイデアを、なぜ私の双方向バインディングは一方向にのみ動作しますか?

+1

を私はあなたがApp' 'ですべてが奇妙' MyDataContext'ものを削除し、ちょうどメインウィンドウコンストラクタか何かでのviewmodelを作成することをおすすめします。また、WPFのコントロールにバインドされるコレクションには常にObservableCollection を使用してください。リストを使用しないでください。内容が変化したときにイベントを発生させません。 –

+2

'ItemsSource =" {バインディングNameList、Mode = TwoWay} "は意味がありません。ItemsControlは決して 'ItemsSource'プロパティを変更しないので、' Mode = TwoWay'の設定は決して影響しません。それ以外にも、NameListインスタンスが変更されないので、 'MyDataContext.OnPropertyChanged(" NameList ");'は暗黙のうちに無視されます。したがってObservableCollectionを使用してください。 – Clemens

+0

紳士 - ありがとう!確かに、私の主なエラーは、ObservableCollectionを使用しないことでした。私はNameListのバインディングで 'Mode = TwoWay'を削除しました。あなたが言及しているように、Clemensは実際には何の効果もありませんでした(明示的にTwoWayモードを設定せずに完全に動作しました)。それを指摘してくれてありがとう。 Edでは、MainWindowでviewmodelの作成を試行します。今の私のコードは、自分自身の実現可能性のチェックでした。それ(と私の限られたWPFの知識)は、珍しい構造につながった。私はそれを訂正します。 – Woelund

答えて

0

バインドされたコレクションにINotifyCollectionChanged(たとえばObservableCollection<T>)が実装されていないと、ビューを更新しようとすると一貫性のない動作や不一致の動作が発生します。チェック状態をtrueに切り替えた後、マウスのスクロールホイールをフリックするとリストが実際に更新されることに気付きました。また、@クレメンスは言ったように、あなたのItemsSourceバインディングはMode=TwoWayでなければなりません。

脇に、INotifyCollectionChanged準拠のコレクションを使用する必要があります。完了したらバインディングをクリアしないとリーク[1]に陥る可能性があるためです。これはシングルウィンドウアプリケーションの問題ではありませんが、今言及する価値はあります。眠る間IsCheckedトグル用として

は、私の推測は、Thread.Sleepは(それを結ぶようにして)、UIスレッド上で起こっているということですので、あなたはPropertyChangedは無用であるデッドタイムの​​6秒を持っています。

private async void Toggle() 
{ 
    MyDataContext.IsChecked = true; 
    await Task.Delay(3000); 
    MyDataContext.IsChecked = false; 
    await Task.Delay(3000); 
    MyDataContext.IsChecked = true; 
} 

Appコンストラクタの終わりにToggle()への呼び出しを:私は(適切なコレクション型を使用していると仮定して)以下でこれを解決することができました。これは残念なことに、アプリケーションが機能しない別のスレッドからコレクションを変更しようとしました。その後、のようなとんでもない何かをを解決することができます:

 ... 
     Toggle(Application.Current.Dispatcher); 
    } 

    private async void Toggle(System.Windows.Threading.Dispatcher d) 
    { 
     d.Invoke(() => { MyDataContext.IsChecked = true; }); 
     await Task.Delay(3000); 
     d.Invoke(() => { MyDataContext.IsChecked = false; }); 
     await Task.Delay(3000); 
     d.Invoke(() => { MyDataContext.IsChecked = true; }); 
    } 

それはちょうどあなたのプログラムの貧困層構造を強制です。 編集:async/awaitを使用するとUIスレッドを解放するという追加の利点があることを忘れていました。チェック状態の間にウィンドウ全体をロックしなくなりました。

コードを適切なファイルに分けて論理を適切な場所に分けることをお勧めします。 HandleDataContextPropertyChangeは、セッターの内部でIsChecked、Notifyコールを除いて発生する可能性があります。

[1] https://blog.jetbrains.com/dotnet/2014/09/04/fighting-common-wpf-memory-leaks-with-dotmemory/

+0

ありがとう、Brandon - 魅力のように動作します!努力のために多くのありがとう - 非常に感謝します。私は今、マウスホイールを動かすことで実際にリストを更新していることも見ています...面白いです。もちろん、私の主な間違いはObservableCollectionを使用しないことでした。私はまた、IsCheckedフラグをトグルするためにAppコンストラクタの最後に新しいスレッドを作成しようとしましたが、同じ問題が発生しました(コレクションは別のスレッドから変更できません)。ディスパッチャを介してトグルする(私は毎日事を学んでいます:-))うまく動作します。 私のコードはフィージビリティチェックです。あなたの提案どおりに構造が改善されます。 – Woelund

関連する問題