Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF repeating elements

I have a UserControl that is a button, with certain characteristics, and I have a window that has several of these buttons in their "normal" style. On this same window, I have defined a style that overrides some of the normal characteristics, and I want a bunch of them (sort of like a keyboard layout). What I have is a UniformGrid with 30 lines like this:

<wft:TouchButton Style="{StaticResource smallButtonStyle}" Click="TouchButton_Click" Tag="1">1</wft:TouchButton>

<wft:TouchButton Style="{StaticResource smallButtonStyle}" Click="TouchButton_Click" Tag="2">2</wft:TouchButton>

<wft:TouchButton Style="{StaticResource smallButtonStyle}" Click="TouchButton_Click" Tag="3">3</wft:TouchButton>

where the only thing changing from line to line is the Tag and Content values. What is a better way to lay out something repetitive like this, where the Style and Click events do not have to be on every line?

like image 713
Kelly Cline Avatar asked Oct 07 '11 14:10

Kelly Cline


3 Answers

A better way would be to create a data-object in code that represents the 30 items that you want in your UI, for example:

class DataObject
{
  string Tag {get;set;}
  string Content {get;set;}
}

(I am sure you can come up with a better name than that!). Then create your 30 items and set them as the ItemsSource of an ItemsControl:

List<DataObject> myObjects = new List<DataObject>();
// populate here
itemsControl.ItemsSource = myObjects

Your ItemsControl has a DataTemplate that is used to render each item:

<ItemsControl x:Name="itemsControl">
  <!-- specify the panel that is the container for your items -->
  <ItemsControl.ItemPanelTemplate>
    <ItemPanelTemplate>
      <UniformGrid/>
    </ItemPanelTemplate>
  </ItemsControl.ItemPanelTemplate>
  <!-- specify the template used to render each item -->
  <ItemsControl.ItemTemplate>
    <DataTemplate>
       <wft:TouchButton Style="{StaticResource smallButtonStyle}"
                        Click="TouchButton_Click"
                        Tag="{Binding Tag}"/
                        Content="{Binding Content}">
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>
like image 95
ColinE Avatar answered Nov 16 '22 21:11

ColinE


I guessed that ColinE's response was off the top of his head, and I gave him a +1 for pointing me in the right direction [THANKS], although it didn't exactly work. What I ended up with was close:

In the constructor for the window, to set up the 30 almost-identical lines:

var numberButtons = Enumerable.Range( 1, 30 )
    .Select( r => new KeyValuePair<string,string>( r.ToString( ), r.ToString( ) ) )
    .ToList( );
numberButtonItems.ItemsSource = numberButtons;

Then, this xaml (note that "Caption" is a property of our user control, so it won't work for you):

<ItemsControl Grid.Row="1" x:Name="numberButtonItems">
    <ItemsControl.ItemsPanel>
    <!-- specify the panel that is the container for your items -->
        <ItemsPanelTemplate>
             <UniformGrid Rows="4" Columns="10" HorizontalAlignment="Left" />
        </ItemsPanelTemplate>
   </ItemsControl.ItemsPanel>
   <!-- specify the template used to render each item -->
   <ItemsControl.ItemTemplate>
        <DataTemplate>
             <wft:TouchButton Style="{StaticResource smallButtonStyle}"
                  Click="TouchButton_Click"
                  Tag="{Binding Key}"
                  Caption="{Binding Value}"/>
        </DataTemplate>
   </ItemsControl.ItemTemplate>
 </ItemsControl>
like image 42
Kelly Cline Avatar answered Nov 16 '22 20:11

Kelly Cline


An alternative (more of a pure xaml) solution would be to use a default style BasedOn your named style, and adding an EventSetter for the ClickEvent:

<UniformGrid>
    <UniformGrid.Resources>
        <Style TargetType="{x:Type wft:TouchButton}" BasedOn="{StaticResource smallButtonStyle}">
            <EventSetter Event="Click" Handler="chButton_Click" />
        </Style>
    </UniformGrid.Resources>
    <wft:TouchButton Tag="1">1</wft:TouchButton>
    <wft:TouchButton Tag="2">2</wft:TouchButton>
    <wft:TouchButton Tag="3">3</wft:TouchButton>
</UniformGrid>

A complete generic example for anyone else who runs across this answer:

Your existing code might look like this:

<Window x:Class="TestWPF.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow">
    <Window.Resources>
        <Style x:Key="myButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="Background" Value="Red" />
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <Button Style="{StaticResource myButtonStyle}" Content="1" Click="Button_Click" />
            <Button Style="{StaticResource myButtonStyle}" Content="2" Click="Button_Click" />
            <Button Style="{StaticResource myButtonStyle}" Content="3" Click="Button_Click" />
            <Button Style="{StaticResource myButtonStyle}" Content="4" Click="Button_Click" />
            <Button Style="{StaticResource myButtonStyle}" Content="5" Click="Button_Click" />
            <Button Style="{StaticResource myButtonStyle}" Content="6" Click="Button_Click" />
        </StackPanel>
    </Grid>
</Window>

And you can change it to the following:

<Window x:Class="TestWPF.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow">
    <Window.Resources>
        <Style x:Key="myButtonStyle" TargetType="{x:Type Button}">
            <Setter Property="Background" Value="Red" />
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <StackPanel.Resources> <!-- Adding style below will apply to all Buttons in the stack panel -->
                <Style TargetType="{x:Type Button}" BasedOn="{StaticResource myButtonStyle}">
                    <EventSetter Event="Click" Handler="Button_Click" />
                </Style>
            </StackPanel.Resources>
            <Button Content="1" />
            <Button Content="2" />
            <Button Content="3" />
            <Button Content="4" />
            <Button Content="5" />
            <Button Content="6" />
        </StackPanel>
    </Grid>
</Window>
like image 32
Scott Avatar answered Nov 16 '22 20:11

Scott