mogmo .NET

C#/XAML/VB6たまにC++についてメモ程度に書いていく。あくまで自分用。責任は取れません。

GridSplitterを使う時の注意

GridSplitter の HorizontalAlignment="Center"を設定してあげないと,挙動が変になる。

正常コード

<Grid Margin="20">
	<Grid.ColumnDefinitions>
		<ColumnDefinition Width="*"/>
		<ColumnDefinition Width="auto"/>
		<ColumnDefinition Width="*"/>
	</Grid.ColumnDefinitions>
	<Grid Grid.Column="0" Background="LightBlue"/>
	<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Center"/>
	<Grid Grid.Column="2" Background="LightPink"/>
</Grid>

f:id:mogmo811:20190424090713p:plain

変なコード

真ん中が割れちゃう。

<Grid Margin="20">
	<Grid.ColumnDefinitions>
		<ColumnDefinition Width="*"/>
		<ColumnDefinition Width="auto"/>
		<ColumnDefinition Width="*"/>
	</Grid.ColumnDefinitions>
	<Grid Grid.Column="0" Background="LightBlue"/>
	<GridSplitter Grid.Column="1" Width="5"/>
	<Grid Grid.Column="2" Background="LightPink"/>
</Grid>

f:id:mogmo811:20190424090759p:plain

ThumbコントロールのMouseDownイベントを発生させる

social.msdn.microsoft.com

確かに,Thumbコントロールの MouseDown は,
イベントは起きないというか,
正確には,論理ツリーをbuble upして伝播していくんですが,
その時にハンドラメソッドが呼ばれないように,
Thumbコントロールが実装時にUIElement.MouseDownを
クラスハンドリング(class handling)して,handled = true してしまっていますね。
(handled == true だと,イベントが伝播してきても,
ふつうのやり方で登録されたハンドラメソッドは呼び出されない)


例えば,そのクラスハンドリング処理からの抜け道を作るため

public Window1()

{

InitializeComponent();


AddHandler(System.Windows.Controls.Primitives.Thumb.MouseDownEvent, new RoutedEventHandler(MyMouseDown), true);

}

void MyMouseDown(object sender, RoutedEventArgs e)

{

MessageBox.Show(string.Format("MyMouseDown: handled == {0}", e.Handled));

}


のように AddHandler してやる
と,
"MyMouseDown: handled == true"
となるので,クラスハンドリング(class handling)していることを確認できます。

なので,bublingイベントが発生してしなかったのですね。

回避方法は,Preview系(tunneling系)イベントを使うか,AddHandler(..., ..., true) を使うか
が,ヘルプによると,推薦のようですね。

# 私もかなり勉強になりました。
# 他のコントロールでもクラスハンドリングしているイベントが多々あると思うので,
# きっと,みんなこれがわからずに,嵌りますね...

ListView/ListBoxの要素間の隙間を無くす

ItemContainerStyleでListBoxItemのスタイルのPaddingを0にすればよい。

<ItemsControl.ItemContainerStyle>
	<Style TargetType="{x:Type ListBoxItem}">
		<Setter Property="BorderThickness" Value="1"/>
		<Setter Property="Margin" Value="0"/>
		<Setter Property="Padding" Value="0"/>
	</Style>
</ItemsControl.ItemContainerStyle>

INotifyPropertyChangedの実装

いつも調べちゃうのでメモ。

INotifyPropertyChangedを継承する

プロパティの変更通知をViewに反映するので,ViewModelに継承させることが多い。
場合によってはModelに継承させることもしばしば。

NugetからPrismパッケージをインストールして使用すると,次の見出しで説明するSetPropertyが標準で実装されているが,Nugetからインストールするのがめんどくさい。

実装する

OnProperty(string propertyName)の実装が基本だが,以下のようにSetPropertyメソッドを実装するほうが使い勝手がいい。

#region INotifyPropertyChanged Support
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
	PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

protected virtual bool SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null)
{
	if (Equals(field, value)) { return false; }
	field = value;
	this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
	return true;
}
#endregion INotifyPropertyChanged Support

使いかた

プロパティのsetterを以下のように記入して使う。

OnPropertyChangedの場合

OnPropertyChangedを呼び出して,Viewに変更通知を行う。

private string _displayText;
public string DisplayText { get => _displayText; set { _displayText = value; OnPropertyChanged(nameof(DisplayText)); } }

SetPropertyの場合

SetPropertyを呼び出せば,プロパティの値変更と変更通知を同時にしてくれる。

private string _displayText;
public string DisplayText { get => _displayText; set => SetProperty(ref _displayText, value); }

コードビハインドのデータをBindingする

方法

コードビハインドに書いたプロパティをBindingさせるには,
RelativeSourceでUserControlやWindow等大元まで参照を戻す必要がある。
これしないとDataContextを参照しようとするからね。

XAML

<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=SampleText}"/>

コードビハインド:

public string SampleText { get; set; }