WPFでデータバインドの簡単な方法を見つけようとしています。
私はINotifyPropertyChanged
インターフェイスを使用しています。抽象基本クラスに実装され、バインドされたメンバーを持つオブジェクトに継承されていれば正常に動作します。WPF INotifyPropertyChanged(ベースクラスを作成せず)
public partial class MainWindow : Window
{
public static MainWindow Instance;
private readonly Vm _vm;
public MainWindow()
{
InitializeComponent();
DataContext = _vm = new Vm
{
Button1 = new Vm.ObservableButton(button1, new List<string> { "Paused", "Logging" }, false),
Button2 = new Vm.ObservableToggleButton(button2, new List<string> { "Log All", "Log VBA" }, false),
};
}
private class Vm
{
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged ([CallerMemberName] string propName = "")
{
var pc = PropertyChanged;
if (pc != null)
pc(this, new PropertyChangedEventArgs(propName));
}
}
public class ObservableButton : ObservableObject
{
private readonly Button _b;
private readonly List<string> _options;
private string _content;
public string Content
{
get { return _content; }
set
{
if (_content == value) return;
_content = value;
OnPropertyChanged();
}
}
public Boolean On { set; private get; }
public ObservableButton (Button b, List<string> options, Boolean on = true)
{
_b = b;
_options = options;
_b.Click += Click;
On = on;
Content = On ? _options[0] : _options[1];
}
public void Click (object sender, RoutedEventArgs e)
{
On = !On;
Content = On ? _options[0] : _options[1];
}
}
public class ObservableToggleButton : ObservableObject
{
private readonly ToggleButton _b;
private readonly List<string> _options;
private string _content;
public string Content
{
get { return _content; }
private set
{
if (_content == value) return;
_content = value;
OnPropertyChanged();
}
}
private Boolean _on;
public Boolean On
{
private get { return _on; }
set
{
if (_on == value) return;
_on = value;
Content = value ? _options[0] : _options[1];
}
}
public ObservableToggleButton (ToggleButton b, List<string> options, Boolean on = true)
{
_b = b;
_options = options;
On = on;
Content = _b.IsChecked ?? false ? _options[0] : _options[1];
}
public void Push()
{
var peer = new ToggleButtonAutomationPeer(_b);
var toggleProvider = peer.GetPattern(PatternInterface.Toggle) as IToggleProvider;
if (toggleProvider != null) toggleProvider.Toggle();
//On = !On;
}
}
public ObservableButton Button1 { get; set; }
public ObservableToggleButton Button2 { get; set; }
public Vm()
{
}
}
}
<Grid Margin="0,0,183,134">
<Button x:Name="button1" Content="{Binding Button1.Content}" HorizontalAlignment="Left" Margin="112,134,0,0" VerticalAlignment="Top" Width="75"/>
<ToggleButton x:Name="button2" IsChecked="{Binding Button2.On, Mode=OneWayToSource}" Content="{Binding Button2.Content}" HorizontalAlignment="Left" Margin="206,134,0,0" VerticalAlignment="Top"/>
</Grid>
でも、私は、基本クラスを燃焼せずにこれをやってみたかったので、私はバックビューモデル上の単一のインタフェースを介して、ビューモデルにINotifyPropertyChanged
を実装し、結合したメンバーから変更イベントをルーティングされました。バインディングオブジェクトにはソースと正しいプロパティ名への参照がありますが、これは暗黙のうちに失敗します。
バインディングオブジェクトで型チェックが行われるため、バインドされたプロパティで偽の実装が作成されたため、機能しないと考えました。ここではそのシナリオのためのコードは、あなたがあるため、彼らObservableButton
とObservableToggleButton
タイプはまだ彼らの親によって変更通知をルーティングしている上のインタフェースは、バインディング・オブジェクトが幸せであっても、ことがわかります...
public partial class MainWindow : Window
{
public static MainWindow Instance;
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel
{
Button1 = new ViewModel.ObservableButton(button1, new List<string> { "Paused", "Logging" }, false),
Button2 = new ViewModel.ObservableToggleButton(button2, new List<string> { "Log All", "Log VBA" }, false),
};
}
public class ViewModel : INotifyPropertyChanged
{
private static ViewModel _instance;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged<T> (T control, [CallerMemberName] string propName = "")
{
var pc = PropertyChanged;
if (pc != null)
pc(control, new PropertyChangedEventArgs(propName));
}
public class ObservableButton : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged() {}
private readonly Button _b;
private readonly List<string> _options;
private string _content;
public string Content
{
get { return _content; }
set
{
if (_content == value) return;
_content = value;
_instance.OnPropertyChanged(this);
}
}
public Boolean On { set; private get; }
public ObservableButton (Button b, List<string> options, Boolean on = true)
{
_b = b;
_options = options;
_b.Click += Click;
On = on;
Content = On ? _options[0] : _options[1];
}
public void Click (object sender, RoutedEventArgs e)
{
On = !On;
Content = On ? _options[0] : _options[1];
}
}
public class ObservableToggleButton : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged() {}
private readonly ToggleButton _b;
private readonly List<string> _options;
private string _content;
public string Content
{
get { return _content; }
private set
{
if (_content == value) return;
_content = value;
_instance.OnPropertyChanged(this);
}
}
private Boolean _on;
public Boolean On
{
private get { return _on; }
set
{
if (_on == value) return;
_on = value;
Content = value ? _options[0] : _options[1];
}
}
public ObservableToggleButton (ToggleButton b, List<string> options, Boolean on = true)
{
_b = b;
_options = options;
On = on;
Content = _b.IsChecked ?? false ? _options[0] : _options[1];
}
}
public ObservableButton Button1 { get; set; }
public ObservableToggleButton Button2 { get; set; }
public ViewModel()
{
_instance = this;
}
}
}
<Grid Margin="0,0,183,134">
<Button x:Name="button1" Content="{Binding Button1.Content}" HorizontalAlignment="Left" Margin="112,134,0,0" VerticalAlignment="Top" Width="75"/>
<ToggleButton x:Name="button2" IsChecked="{Binding Button2.On, Mode=OneWayToSource}" Content="{Binding Button2.Content}" HorizontalAlignment="Left" Margin="206,134,0,0" VerticalAlignment="Top"/>
</Grid>
ですつま先をタイプします。
子オブジェクトがそれを使わずにバインディングを完了する必要があるのに、インターフェイスを実装する必要がある理由はありますか?
なぜ親から何かを正確にルーティングしたいのですか?この質問には直接関係はありませんが、コントロールインスタンス(この場合はButton)をビューモデルに渡しているため、ビュービューモデルパターン全体の目的が損なわれ、役に立たなくなります。上記のコードブロック全体と質問自体は、あなたがそのパターンを打ち破っていないことを示しています(または、設計通りに動作したくない)ことを示しています。たぶんそれは最初に修正する必要があります。 – Evk
@Evkの場合、最初の例では親を通して何もルーティングされていませんが、シグナルは抽象基底クラスから継承したインタフェースを介してボタンとバインディングの間に直接あります。基底クラスはどこにでも定義することができますが、私はモデルのコンパクトさのためにView Modelでそれを持っています。 –
ViewModelのUI要素を保持するだけで、MVVMパターンが完全に壊れてしまいます...私もxyに直面していると思います。http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem – Mat