2013-05-09 10 views
7

カスタムユーザーコントロールでデータバインドに問題があります。私は私の問題を強調するサンプルプロジェクトを作成しました。私は完全にWPFと本質的にMVVMにも新しかったので、私に同行してください。カスタムユーザーコントロールのDependencyPropertyにバインドしても変更時に更新されない

私はデータバインディングの2つの方法を使用する単純なビューを作成しました。組み込みコントロールのデータバインディングは正常に機能します。私のカスタムコントロールは...私のコントロールのPropertyChangedCallbackにブレークポイントを置く。スタートアップ時に1度ヒットしますが、その後はもう一度ヒットしません。一方、私が同じ価値観に縛られたラベルは、うれしくカウントダウンしています。

私には何が欠けていますか?私のサンプルプロジェクトは、次のとおりです。

メインウィンドウ:

<Window x:Class="WpfMVVMApp.MainWindow" 
     xmlns:local="clr-namespace:WpfMVVMApp" 
     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> 
     <Grid.DataContext> 
      <local:CountdownViewModel /> 
     </Grid.DataContext> 
     <Label Name="custName" Content="{Binding Path=Countdown.ChargeTimeRemaining_Mins}" Height="45" VerticalAlignment="Top"></Label> 
     <local:UserControl1 MinutesRemaining="{Binding Path=Countdown.ChargeTimeRemaining_Mins}" Height="45"></local:UserControl1> 
    </Grid> 
</Window> 

はここに私のモデルです:

namespace WpfMVVMApp 
{ 

    public class CountdownModel : INotifyPropertyChanged 
    { 
     private int chargeTimeRemaining_Mins; 
     public int ChargeTimeRemaining_Mins 
     { 
      get 
      { 
       return chargeTimeRemaining_Mins; 
      } 
      set 
      { 
       chargeTimeRemaining_Mins = value; 
       OnPropertyChanged("ChargeTimeRemaining_Mins"); 
      } 
     } 

     #region INotifyPropertyChanged Members 
     public event PropertyChangedEventHandler PropertyChanged; 
     private void OnPropertyChanged(string propertyName) 
     { 
      if (PropertyChanged != null) 
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
     #endregion 
    } 
} 

のViewModel:

namespace WpfMVVMApp 
{ 
    public class CountdownViewModel 
    { 
     public CountdownModel Countdown { get; set; } 

     DispatcherTimer timer; 
     private const int maxMins = 360; 

     public CountdownViewModel() 
     { 
      Countdown = new CountdownModel { ChargeTimeRemaining_Mins = 60 }; 

      // Setup timers 
      timer = new DispatcherTimer(); 
      timer.Tick += new EventHandler(this.SystemChargeTimerService); 
      timer.Interval = new TimeSpan(0, 0, 1); 
      timer.Start(); 
     } 

     private void SystemChargeTimerService(object sender, EventArgs e) 
     { 
      //convert to minutes remaining 
      // DEMO CODE - TODO: Remove 
      this.Countdown.ChargeTimeRemaining_Mins -= 1; 
     } 
    } 
} 

ここに私のユーザーコントロールのXAMLです:

<UserControl x:Class="WpfMVVMApp.UserControl1" 
      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="300" d:DesignWidth="300"> 
    <Grid> 
     <Label Name="Readout"></Label> 
    </Grid> 
</UserControl> 

そしてここでは、ユーザーコントロールの背後にあるコードです:

namespace WpfMVVMApp 
{ 
    public partial class UserControl1 : UserControl 
    { 
     #region Dependency Properties 
     public static readonly DependencyProperty MinutesRemainingProperty = 
        DependencyProperty.Register 
        (
         "MinutesRemaining", typeof(int), typeof(UserControl1), 
         new UIPropertyMetadata(10, new PropertyChangedCallback(minutesRemainChangedCallBack)) 
        ); 
     #endregion 

     public int MinutesRemaining 
     { 
      get 
      { 
       return (int)GetValue(MinutesRemainingProperty); 
      } 
      set 
      { 
       SetValue(MinutesRemainingProperty, value); 
      } 
     } 

     static void minutesRemainChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args) 
     { 
      UserControl1 _readout = (UserControl1)property; 
      _readout.MinutesRemaining = (int)args.NewValue; 

      _readout.Readout.Content = _readout.MinutesRemaining; 
     } 

     public UserControl1() 
     { 
      InitializeComponent(); 
     } 
    } 
} 

答えて

10

あなたの変更コールバックが結合破壊されます。

スケルトンとして:あなたのウィンドウにはUC.X="{Binding A}"があり、そのプロパティでは(UCで)X=B;に変更されています。どちらの場合もXに設定されているため、バインドが解除されます。 、修正変更コールバックを削除し、ラベルにこれを追加するには

Content="{Binding MinutesRemaining, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" 
+0

応答をいただき、ありがとうございます。バインドされていた動作します。しかし、私が依存関係プロパティを作成した理由は、実際のプログラムでラベルを設定するだけではない(これは単なる例にすぎません)。 変更コールバックでMinutesRemainingに値を割り当てていたことがわかりました。これは元のバインディングを破ります。私はその行をコメントアウトし、今は期待どおりに動作します。 – user2367999

2

私はあなたのコードを試してみましたが、正常に動作します私が作った唯一の変更は、あなたが持っているpropertychangedcallbackとデータバインドラベルの後ろにコードを削除しました(読み出し)を依存関係プロパティに追加します。

ユーザーコントロール(XAML)

<UserControl x:Class="WpfApplication1.UserControl" 
     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="300" d:DesignWidth="300"> 
    <Grid> 
     <Label Name="Readout" Content="{Binding RelativeSource={RelativeSource 
          AncestorType=UserControl}, Path=MinutesRemaining}"/> 
    </Grid> 
</UserControl> 

ユーザーコントロール(BEHIND CODE)

public partial class UserControl1 : UserControl 
{ 
    #region Dependency Properties 
    public static readonly DependencyProperty MinutesRemainingProperty = 
       DependencyProperty.Register 
       (
        "MinutesRemaining", typeof(int), typeof(UserControl1), 
        new UIPropertyMetadata(10) 
       ); 
    #endregion 

    public int MinutesRemaining 
    { 
     get 
     { 
      return (int)GetValue(MinutesRemainingProperty); 
     } 
     set 
     { 
      SetValue(MinutesRemainingProperty, value); 
     } 
    } 

    public UserControl1() 
    { 
     InitializeComponent(); 
    } 
} 
関連する問題