WPFのメモ
visual studioでプロジェクト作成すると最初から作られているファイル。
Application.StartupUri
がアプリケーション起動時の読み込み画面
以下みたいに画面にまとまったパーツを配置する場合、Gridとformを使いページの挿入で配置するのがいい?
そうすれば1つのxamlにすべてのフォームを書くことなく、ページというxaml単位で管理していける。
が一番いい方法かはわからない。
以下みたいに画面にまとまったパーツを配置する場合、Gridとformを使いページの挿入で配置するのがいい?
UserControlで分割したほうがいい。ページは画面遷移をしたい意図で使うべきとのこと。frameとか差し込み方がURIクラスを使用しているので確かにその通り。
e.g) frame.Source = new Uri("/SearchPage.xaml", UriKind.Relative);
また、Gridじゃなくても、<StackPanel>と<WrapPanel>で領域を分けることができる。radio buttonはパネルでグールピングされるのでこっちのほうが都合もいい?
Windowに配置したUserControlのイベントからその配置したWindowのコントロールを操作するにはどうすればいい?
操作できなくても、WindowからUserControlのイベントを検知するにはどうすればいい?
やりたいこと。
以下のようなWindowで
- ButtonはUserControlから設定(水色部分がそう)
- LabelはMainWindowで設定
ボタンを押したらMainWindowの★マークを変更したい。
<local:>
にButtonBase.Click="ChangeCircle"
を追加すればいい。
→delegateやEventHandlerとしてイベント追加はよくわかってない。
MainWindow.xaml
<Window x:Class="test.MainWindow"
//~略~
>
<Grid>
<StackPanel >
<local:MyUserControl1 ButtonBase.Click="ChangeMark"/>
<WrapPanel>
<Label>ボタンを押すとここが変わります→</Label>
<Label x:Name="ChangeLabel">★</Label>
</WrapPanel>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
// classにイベントの関数を定義する
private void ChangeMark(object sender, RoutedEventArgs e)
{
this.ChangeLabel.Content = "●";
}
ButtonBase.Click=""
だとそのUserControlのボタンクリックイベントすべてを検知する。
ボタンが複数ある場合は次のようにする。
- UserControl.xamlで、ボタンにNameを設定する
<UserControl
~略~
>
<Button x:Name="changeMaru" Content="maru"/>
<Button x:Name="changeSikaku" Content="sikaku"/>
</UserControl>
- MainWindow.xaml.csで
e.OriginalSource as Button
を取得しNameで識別する
private void ChangeMark(object sender, RoutedEventArgs e)
{
var button = e.OriginalSource as Button;
if (button.Name == "changeMaru")
{
this.ChangeLabel.Content = "●";
}
else if(button.Name == "changeSikaku")
{
this.ChangeLabel.Content = "■";
}
}
すべてのボタンを検知してしまうので、検知したいボタンだけをリッスンする方法はどうやる?
これはMVVMとUserControlをViewModelでプロパティとして持っておくやり方がベスト。
<DataTemplate>に名前を付けてもソースコードからはアクセスできない。
事前に定義不可だが、xamlで追加すること自体はvs上でエラーは出ない。
<ListView x:Name="previewListView" Grid.Column="1" Grid.Row="1" SelectionMode="Extended" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid x:Name="viewCrid" Width="300" Height="200">
<!-- ここにデータを追加する -->
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
DataTemplate > Gridに x:Name=.viewCrid
としているが単純にその名前を使うだけではコードからアクセスできない。
以下の方法でアクセスできる。
ContentPresenter を見つけて、その ContentPresenter に設定されている DataTemplate 上で FindName を呼び出す必要があります。
ListeViewでのやりかたがわからない、、
コントロールのイベント通知にはDelegateCommandを使うの良さそう。
プロパティの変更通知にはSetPropertyを使うと楽。
上記2つPrismフレームワークなので、nugetでインストールして使う
プロパティの通知はこんなに簡単になります。
public string Data{
get => this._Data;
set {
this._Data = value;
RaisePropertyChanged("Data");
}
}
public string Data{
get => this._Data;
set => SetProperty(ref this._Data, value);
}
草
Prismでプロパティが変わったときにコントロールのCanExecuteを実行する方法メモ
// プロパティ
public bool IsProcessing1 {
get { return _isProcessing1; }
set { SetProperty(ref _isProcessing, value); }
}
// プロパティ
public bool IsProcessing2 {
get { return _isProcessing2; }
set { SetProperty(ref _isProcessing2, value); }
}
public DelegateCommand<object> EditButton_Click_Command { get; private set; }
// コンストラクタ
public Test(){
this.EditButton_Click_Command = new DelegateCommand<object>(EditButton_Click, CanEditButton_Click)
.ObservesProperty(() => IsProcessing1) // 監視対象のプロパティ設定
.ObservesProperty(() => IsProcessing2); // メソッドチェーンで複数指定可能
}
void EditButton_Click(object param){
// 処理
}
// 実行可能判定に使用するメソッド
// ObservesPropertyで指定したプロパティが変わった場合にも実行される
bool CanEditButton_Click(object param) {
return !(IsProcessing1|| IsProcessing2);
}
第2引数のメソッド(上の例ならCanEditButton_Clickメソッド)の返り値がtrueなら押下可能、falseなら押下不可。
また、DelegateCommandの第2引数を指定しない場合は常にtrueになる。
VMの基底クラスでDelegateCommandの登録様式は定義しておいて、後からCommandを追加したい場合。
以下サンプル。(いい実装かはわからない)
class ButtonControlVMBase{
public DelegateCommand SubmitButton_Click_Command { get; set; }
/// <summary>
/// SubmitButton_Click 格納プロパティ<br/>
/// ボタン押下イベントのメソッドを設定します。
/// デフォルトでは未設定であるというExceptionが発生する<see cref="SubmitButton_Click_Default"/>が実行されます。
/// </summary>
public Action SubmitButton_Click_Event { get; set; }
public ButtonControlVMBase() {
SubmitButton_Click_Event = SubmitButton_Click_Default;
}
/// <summary>
/// SubmitButton_Click_EventをDelegateCommandに登録します。<br/>
/// <see cref="SubmitButton_Click_Event"/> に実行したいメソッドを設定した後に呼んでください。
/// </summary>
public void SetDelegateCommand() {
SubmitButton_Click_Command = new DelegateCommand(SubmitButton_Click_Event, Can_Button_Click);
}
void SubmitButton_Click_Default() {
throw new MyHogeHogeException("登録ボタンイベントをプロパティに設定してください。");
}
bool Can_Button_Click() {
return true;
}
}
あとは、呼び出し側でSubmitButton_Click_Event
にメソッドを設定した後、SetDelegateCommand()
を読んであげればいい。
上記はもし未設定のままであれば、独自のExceptionが発生するコマンドを登録している。
もっとシンプルなら単純にDelegateCommandのプロパティを用意しておけばいい。
class ButtonControlVMBase{
public DelegateCommand SubmitButton_Click_Command { get; set; }
}
class ButtonControlViewModel : ButtonControlVMBase{
public ButtonControlViewModel (){
SubmitButton_Click_Command = new DelegateCommand(Hogehoge);
}
}
バインディングデータの型で表示するコントロールを可変にする方法。
前提
MyUserControlViewModel型のコレクションMyData
変数があるとする。
MyDatasの要素には以下3種のViewModelクラスのインスタンスが格納されているものとする。
- TextViewModel
- CheckboxViewModel
- MyUserControlViewModel
要件はこのMyData
変数をListViewなどで表示するとき、要素の型によって表示するコントロールを可変としたい。
使用するもの
-
UserControl.Resources
(xxx.Resourcesに定義できればOK) - UserControl.Resources -> DataTemplate.DataType
- ここにコード例の通りに対象の型を記載してあげる
- ItemsControl
- ContentControl
コード例
<UserControl
~省略~
xmlns:myvm="clr-namespace:myviewmodel"
>
<UserControl.Resources>
<DataTemplate DataType="{x:Type myvm:TextViewModel}">
<TextBlock Text={Binding TextData}/>
</DataTemplate>
<DataTemplate DataType="{x:Type myvm:CheckboxViewModel}">
<Checkbox IsChecked="{Binding CheckedFlag}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type myvm:MyUserControlViewModel}">
<local:MyUserControl DataContext="{Binding}"/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding MyData}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
要約
Resourcesでコントロールの装飾をするとき、装飾対象を型でしていすればいい。
<ContentControl>はすべてのコントロールの親であり、overrideできるようなものという考え。
参考
ListVIewやItemsControlが並べるデータのViewを指定する仕組みについて簡単に解説します。 ItemsControlではItemsSourceに含まれるデータの型によってViewを選択します。 型が指定されており、Keyが指定されていないResourceが対象となります。(<DataTemaplte TargetType = "{x:type hoge}">みたいなやつ)
https://p4j4.hatenablog.com/entry/2020/04/09/173456