2013年01月15日

[C# WPF]色選択コンボボックスを作る

今回は色選択コンボボックスを作ってみます。

色選択ダイアログもありますが、
いくつかの独自に用意した色の中から
ひとつを選びたいという時はComboBoxのほうがいいと思います。

以下サンプルです。

xamlです。

<Window.Resources>
<x:Array x:Key="ColorItems" Type="s:Color">
<s:Color>Black</s:Color>
<s:Color>Red</s:Color>
<s:Color>Blue</s:Color>
<s:Color>Yellow</s:Color>
<s:Color>Green</s:Color>
</x:Array>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{StaticResource ColorItems}"
SelectedValue="{Binding Color, UpdateSourceTrigger=PropertyChanged}"
Width="90" Height="23" Margin="5">
<ComboBox.ItemTemplate>
<DataTemplate>
<Border Width="80" Height="20" Margin="5,0">
<Border.Background>
<s:SolidColorBrush Color="{Binding}"/>
</Border.Background>
</Border>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="Text" Height="23" Margin="5">
<TextBlock.Foreground>
<s:SolidColorBrush Color="{Binding Color}"/>
</TextBlock.Foreground>
</TextBlock>
</StackPanel>
</Grid>


ソースです。

public partial class MainWindow : Window, INotifyPropertyChanged
{
private Color color = Colors.Black;
public Color Color
{
get { return color; }
set
{
color = value;
OnPropertyChanged("Color");
}
}

public MainWindow()
{
InitializeComponent();

this.DataContext = this;
}

public event PropertyChangedEventHandler PropertyChanged = (s, e) => { };

private void OnPropertyChanged(string name)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}


色の定義もxamlに記述しています。
TextBlockの文字色がComboBoxで選択した色になります。
ソースはBindするプロパティを定義してあるだけです。

実行結果は以下のようになります。
2.jpg

DataGridComboBoxColumnの場合は以下のようにします。

xamlです。

<Window.Resources>
<x:Array x:Key="ColorItems" Type="s:Color">
<s:Color>Black</s:Color>
<s:Color>Red</s:Color>
<s:Color>Blue</s:Color>
<s:Color>Yellow</s:Color>
<s:Color>Green</s:Color>
</x:Array>
<x:Array x:Key="DataGridItems" Type="local:TestClass">
<local:TestClass Color="Black" Text="AAA"/>
<local:TestClass Color="Black" Text="BBB"/>
<local:TestClass Color="Black" Text="CCC"/>
</x:Array>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{StaticResource DataGridItems}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="色" ItemsSource="{StaticResource ColorItems}"
SelectedValueBinding="{Binding Color, UpdateSourceTrigger=PropertyChanged}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border Width="80" Height="20" Margin="5,0">
<Border.Background>
<s:SolidColorBrush Color="{Binding}"/>
</Border.Background>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border Width="80" Height="20" Margin="5,0">
<Border.Background>
<s:SolidColorBrush Color="{Binding}"/>
</Border.Background>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
<DataGridTextColumn Header="テキスト" Binding="{Binding Text}" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground">
<Setter.Value>
<s:SolidColorBrush Color="{Binding Color}"/>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>


ソースです。

public class TestClass : INotifyPropertyChanged
{
private Color color = Colors.Black;
public Color Color
{
get { return color; }
set
{
color = value;
OnPropertyChanged("Color");
}
}

public string Text { get; set; }

public TestClass()
{
}

public event PropertyChangedEventHandler PropertyChanged = (s, e) => { };

private void OnPropertyChanged(string name)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}


ソースはDataGridのItemsSourceにするTestClass部分のみです。
DataGridのItemsSourceの定義もxamlに記述しています。

実行結果は以下のようになります。
2.jpg


posted by cw at 23:30| Comment(1) | TrackBack(0) | Tips | このブログの読者になる | 更新情報をチェックする

[C# WPF]キャンバスの倍率を設定できるようにする

Canvasにはさまざまな図形を配置する事ができますが、
今回はCanvasの表示倍率を変更できるようにします。

サンプルです。

[xaml]

<Window.Resources>
<x:Array x:Key="ScaleItems" Type="s:Int32">
<s:Int32>200</s:Int32>
<s:Int32>100</s:Int32>
<s:Int32>75</s:Int32>
<s:Int32>50</s:Int32>
<s:Int32>25</s:Int32>
</x:Array>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ComboBox ItemsSource="{StaticResource ScaleItems}"
SelectedValue="{Binding Scale, UpdateSourceTrigger=PropertyChanged}"
Width="70" Height="23" Margin="5" HorizontalAlignment="Left"/>
<Canvas Grid.Row="1">
<Canvas.RenderTransform>
<ScaleTransform ScaleX="{Binding CanvasScale}" ScaleY="{Binding CanvasScale}"/>
</Canvas.RenderTransform>
<Rectangle Canvas.Left="100" Canvas.Top="100" Width="100" Height="40" Fill="Red"/>
<Ellipse Canvas.Left="200" Canvas.Top="30" Width="80" Height="80" Fill="Blue"/>
</Canvas>
</Grid>


[ソース]

public partial class MainWindow : Window, INotifyPropertyChanged
{
public int Scale
{
get { return (int)CanvasScale * 100; }
set { CanvasScale = value / 100.0; }
}

private double canvasScale = 1.0;
public double CanvasScale
{
get { return canvasScale; }
set
{
if (value > 0)
{
canvasScale = value;
}
OnPropertyChanged("CanvasScale");
}
}

public MainWindow()
{
InitializeComponent();

this.DataContext = this;
}

public event PropertyChangedEventHandler PropertyChanged = (s, e) => { };

private void OnPropertyChanged(string name)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}


ComboBoxで倍率を設定します。
ComboBoxのリソースはxamlで定義しています。

倍率はCanvas.RenderTransformのScaleTransformに設定します。
設定値は0.0〜1.0の値になります。

ComboBoxで設定する値は%の値なので
ソースのプロパティで相互変換しています。

実行結果は以下のようになります。
3.jpg
1.jpg
2.jpg

25%、100%、200%選択時です。
posted by cw at 23:00| Comment(3) | TrackBack(0) | Tips | このブログの読者になる | 更新情報をチェックする

[C# WPF]アイコンリストを作る

今回はアイコンリストを作ってみます。

サンプルです。

[xaml]

<Window.Resources>
<x:Array x:Key="ListItems" Type="local:ListItem">
<local:ListItem Name="Item1"/>
<local:ListItem Name="Item2"/>
<local:ListItem Name="Item3"/>
<local:ListItem Name="Item4"/>
<local:ListItem Name="Item5"/>
</x:Array>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<GridSplitter Width="3" HorizontalAlignment="Right" VerticalAlignment="Stretch"/>
<ListView Grid.Column="1" ItemsSource="{StaticResource ListItems}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}" Width="50" Height="50" Margin="5"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ListView}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</Grid>


[ソース]

public class ListItem
{
public string Name { get; set; }

public ListItem()
{
}
}


ソースにはアイコンの元になる名前のみをもったクラスを定義しています。

xamlではListのItemTemplateでButtonを指定しています。
Contentに名前をBindしていますが、アイコンにすると見栄えもよくなります。
また、GridSplitterでアイコンリストの幅を大きくした時に
できるだけ多くのアイコンを表示できるようにする為に
ItemsPanelにWrapPanelを指定しています。

実行結果です。
1.jpg

GridSplitterでリストの幅を変更すると以下のようになります。
2.jpg
posted by cw at 22:30| Comment(0) | TrackBack(0) | Tips | このブログの読者になる | 更新情報をチェックする

[C# WPF]アイコンリストをグループ表示する

前回アイコンリストを作りましたが、
今回はこのアイコンリストをグループ表示してみます。

サンプルです。

[xaml]

<Window.Resources>
<x:Array x:Key="ListItems" Type="local:ListItem">
<local:ListItem Name="Item1" GroupName="Group1"/>
<local:ListItem Name="Item2" GroupName="Group1"/>
<local:ListItem Name="Item3" GroupName="Group1"/>
<local:ListItem Name="Item4" GroupName="Group2"/>
<local:ListItem Name="Item5" GroupName="Group2"/>
</x:Array>
<CollectionViewSource x:Key="GroupItems" Source="{StaticResource ListItems}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="GroupName"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<GridSplitter Width="3" HorizontalAlignment="Right" VerticalAlignment="Stretch"/>
<ListView Grid.Column="1" ItemsSource="{Binding Source={StaticResource GroupItems}}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}" Width="50" Height="50" Margin="5"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=ListView}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<Expander Header="{Binding}">
<Expander.Content>
<ItemsPresenter/>
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>


[ソース]

public class ListItem
{
public string GroupName { get; set; }
public string Name { get; set; }

public ListItem()
{
}
}


ソースでは前回のサンプルのListItemクラスにGroupNameを追加しています。

xamlではListViewのItemsSourceをCollectionViewSourceにしています。
CollectionViewSourceはSourceのCollectionをグループ分けする事ができます。
グループ化の為のキーをPropertyGroupDescriptionのPropertyNameに指定します。

ListViewではGroupStyleを設定しています。
グループ化したCollectionを表示するのにExpanderを指定しています。

実行結果は以下のようになります。
2.jpg

展開すると以下のようになります。
1.jpg
posted by cw at 22:00| Comment(0) | TrackBack(0) | Tips | このブログの読者になる | 更新情報をチェックする

[C# WPF]DataGridの列幅を同期する

今回は2つのDataGridの列幅を同期するサンプルを紹介します。

一方のDataGridの列幅をもう一方のDataGrid列幅に
Bindする方法が思いつきますが実はそれではダメです。


<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<DataGrid x:Name="dataGrid1" AutoGenerateColumns="False" Margin="5">
<DataGrid.Columns>
<DataGridTextColumn x:Name="Column1" Header="Data1"/>
<DataGridTextColumn x:Name="Column2" Header="Data2"/>
<DataGridTextColumn x:Name="Column3" Header="Data3"/>
<DataGridTextColumn x:Name="Column4" Header="Data4"/>
<DataGridTextColumn x:Name="Column5" Header="Data5"/>
</DataGrid.Columns>
</DataGrid>
<GridSplitter Grid.Row="1" Height="3" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
<DataGrid x:Name="dataGrid2" Grid.Row="1" AutoGenerateColumns="False" Margin="5">
<DataGrid.Columns>
<DataGridTextColumn Header="Data1"
Width="{Binding ActualWidth, ElementName=Column1}"/>
<DataGridTextColumn Header="Data2"
Width="{Binding ActualWidth, ElementName=Column2}"/>
<DataGridTextColumn Header="Data3"
Width="{Binding ActualWidth, ElementName=Column3}"/>
<DataGridTextColumn Header="Data4"
Width="{Binding ActualWidth, ElementName=Column4}"/>
<DataGridTextColumn Header="Data5"
Width="{Binding ActualWidth, ElementName=Column5}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>


ElementNameで同期する対象列の名前を指定してActualWidthをBindしています。
この方法で同期できそうですができません。

実行結果です。
1.jpg

DataGrid列はソースコードでBindするコードを書かなければ同期できません。


public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();

dataGrid1.BindDataGridColumnsWidth(dataGrid2);
}
}

public static class DataGridEx
{
public static void BindDataGridColumnWidth(this DataGridColumn col1, DataGridColumn col2)
{
var b = new Binding("Width");
b.Source = col2;
b.Mode = BindingMode.TwoWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(col1, DataGridColumn.WidthProperty, b);
}

public static void BindDataGridColumnsWidth(this DataGrid dg1, DataGrid dg2)
{
for (int i = 0; i < dg1.Columns.Count; i++)
{
dg1.Columns[i].BindDataGridColumnWidth(dg2.Columns[i]);
}
}
}


列を指定してBindする処理とDataGridの全ての列をBindする処理を
拡張メソッドを使って作ってみました。

実行結果です。
1.jpg
posted by cw at 21:30| Comment(0) | TrackBack(0) | Tips | このブログの読者になる | 更新情報をチェックする

[C# WPF]DataGridのスクロール位置を同期する

前回はDataGridの列幅を同期しました。
今回はスクロール位置を同期してみます。

DataGridにはスクロール位置のプロパティはないので
xamlで単純にBindはできません。

今回もソースコードで同期する処理を記述します。

[xaml]

<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<DataGrid x:Name="dataGrid1" ItemsSource="{Binding Items}" AutoGenerateColumns="False" Margin="5">
<DataGrid.Columns>
<DataGridTextColumn Header="Data1" Binding="{Binding Data1}"/>
<DataGridTextColumn Header="Data2" Binding="{Binding Data2}"/>
<DataGridTextColumn Header="Data3" Binding="{Binding Data3}"/>
<DataGridTextColumn Header="Data4" Binding="{Binding Data4}"/>
<DataGridTextColumn Header="Data5" Binding="{Binding Data5}"/>
</DataGrid.Columns>
</DataGrid>
<GridSplitter Grid.Row="1" Height="3" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
<DataGrid Grid.Row="1" x:Name="dataGrid2" ItemsSource="{Binding Items}" AutoGenerateColumns="False" Margin="5">
<DataGrid.Columns>
<DataGridTextColumn Header="Data1" Binding="{Binding Data1}"/>
<DataGridTextColumn Header="Data2" Binding="{Binding Data2}"/>
<DataGridTextColumn Header="Data3" Binding="{Binding Data3}"/>
<DataGridTextColumn Header="Data4" Binding="{Binding Data4}"/>
<DataGridTextColumn Header="Data5" Binding="{Binding Data5}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>


[ソース]

public partial class MainWindow : Window
{
private DataGridScrollSynchronizer ScrollSync;

private ObservableCollection items = new ObservableCollection();
public ObservableCollection Items { get { return items; } }

public MainWindow()
{
InitializeComponent();

this.DataContext = this;

for (int i = 0; i < 30; i++)
{
var d = new TestClass();
d.Data1 = i * 10 + 1;
d.Data2 = i * 10 + 2;
d.Data3 = i * 10 + 3;
d.Data4 = i * 10 + 4;
d.Data5 = i * 10 + 5;
Items.Add(d);
}
}

private void Window_Loaded(object sender, RoutedEventArgs e)
{
ScrollSync = new DataGridScrollSynchronizer(dataGrid1, dataGrid2);
ScrollSync.Direction = SynchronizeDirection.Vertical;
}
}

public class TestClass
{
public int Data1 { get; set; }
public int Data2 { get; set; }
public int Data3 { get; set; }
public int Data4 { get; set; }
public int Data5 { get; set; }

public TestClass()
{
}
}

[Flags]
public enum SynchronizeDirection
{
Horizontal = 0x01,
Vertical = 0x02,
Both = 0x03,
}

public class DataGridScrollSynchronizer
{
private ScrollViewer sv1;
private ScrollViewer sv2;

public SynchronizeDirection Direction { get; set; }

public DataGridScrollSynchronizer(DataGrid dg1, DataGrid dg2)
{
sv1 = GetScrollViewer(dg1);
sv2 = GetScrollViewer(dg2);

sv1.ScrollChanged += sv1_ScrollChanged;
sv2.ScrollChanged += sv2_ScrollChanged;
}

private ScrollViewer GetScrollViewer(FrameworkElement fe)
{
int n = VisualTreeHelper.GetChildrenCount(fe);
for (int i = 0; i < n; i++)
{
var child = VisualTreeHelper.GetChild(fe, i) as FrameworkElement;
if (child == null)
{
continue;
}

if (child is ScrollViewer)
{
return (ScrollViewer)child;
}

var sv = GetScrollViewer(child);
if (sv != null)
{
return sv;
}
}
return null;
}

private void sv1_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (Direction.HasFlag(SynchronizeDirection.Horizontal))
{
sv2.ScrollToHorizontalOffset(sv1.HorizontalOffset);
}
if (Direction.HasFlag(SynchronizeDirection.Vertical))
{
sv2.ScrollToVerticalOffset(sv1.VerticalOffset);
}
}

private void sv2_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (Direction.HasFlag(SynchronizeDirection.Horizontal))
{
sv1.ScrollToHorizontalOffset(sv2.HorizontalOffset);
}
if (Direction.HasFlag(SynchronizeDirection.Vertical))
{
sv1.ScrollToVerticalOffset(sv2.VerticalOffset);
}
}
}


今回は表示するデータクラスを用意しています。
スクロールバーが表示されるように要素数を多めに作成しています。

スクロール位置の同期をする別クラスを作りました。
同期する方向も指定できるようにしています。
同期したいDataGridを指定するだけなので使いまわせると思います。

実行結果です。
1.jpg
posted by cw at 21:00| Comment(2) | TrackBack(0) | Tips | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は180日以上新しい記事の投稿がないブログに表示されております。