web-dev-qa-db-ja.com

カスタムユーザーコントロールのDependencyPropertyのバインドが変更時に更新されない

カスタムユーザーコントロールのデータバインディングに問題があります。問題を強調するためにサンプルプロジェクトを作成しました。私はWPFと本質的にMVVMにまったく慣れていないので、我慢してください...

データバインディングを2つの方法で使用する単純なビューを作成しました。組み込みコントロールのデータバインディングは問題なく機能します。カスタムコントロールが機能しません...コントロールのPropertyChangedCallbackにブレークポイントを設定しました。起動時に一度ヒットしますが、二度とヒットしません。その間、私が同じ値にバインドしたラベルは喜んでカウントダウンしています。

何が足りないのですか?私のサンプルプロジェクトは次のとおりです。

メインウィンドウ:

<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();
        }
    }
}
12
user2367999

変更コールバックがバインディングを解除しています。

スケルトンとして:ウィンドウにはUC.X="{Binding A}"があり、そのプロパティの変更(UC)にはX=B;があります。どちらの場合もXを設定するため、これによりバインディングが解除されます。

修正するには、変更コールバックを削除し、これをラベルに追加します。

 Content="{Binding MinutesRemaining, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
13
XAMeLi

私はあなたのコードがうまく機能することを試みました。私が行った唯一の変更は、あなたが持っているpropertychangedcallbackの背後にあるコードを削除し、ラベル(読み出し)を依存関係プロパティにデータバインドすることでした。

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

SERCONTROL(CODE BEHIND)

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();
    }
}
4
Nivid Dholakia