How to arrange controls horizontally with even spacing in WPF - wpf

I am looking for a flexible way to arrange rectangular controls inside a grid with an even spacing. The controls are added/removed dynamically. Eventually I wish to let user select one of the layouts below:
I am thinking of using UniformGrid and let the rest be handled by grid's properties:
<UniformGrid>
<Button Content="Button 1"/>
<Button Content="Button 2"/>
<Button Content="Button 3"/>
<Button Content="Button 4"/>
<Button Content="Button 5"/>
</UniformGrid>
However, my concern is my future ability to customize the layout as in option 3 (see image above). I am looking for suggestions of how to make this as flexible as possible considering I am following MVVM in my design.

Use a UniformGrid and bind the count for Columns and Rows. Use it as the panel for an items control and you can get MVVM separation.
<ItemsControl ItemsSource={Binding X}>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows={Binding...} />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

A WrapPanel can be used to display any number of items, and will automatically wrap to another row if the items exceed the allowed width.
<WrapPanel>
<Button Content="Button 1"/>
<Button Content="Button 2"/>
<Button Content="Button 3"/>
<Button Content="Button 4"/>
<Button Content="Button 5"/>
</WrapPanel>
Alternatively if you're looking for something more structured where you can define a dynamic layout for controls, I'd suggest an ItemsControl with the ItemsPanelTemplate set to either a UniformGrid, WrapPanel, or Grid depending on your requirements. James Lucas' answer provides a good example for that.

Related

WPF how to set stackpanel as resources and reuse it in TabControls

I am new to C# and WPF so please give me some ideas:
I have an WPF app used to display some stack panels,all stack panels default Visibility is set to collapsed and they will switch to visible according to the received data.
Now I want to make all these stack panels to resources so I can reuse it in some new added tab controls and stack panels.
<StackPanel x:Name="ColorOption" Visibility="Collapsed">
<TextBlock Text="Line Color" Style="{StaticResource ItemNameTextBlockStyle}"/>
<Button Style="{StaticResource ColorButtonStyle}" Click="Color_Click">
<Button.Content>
<Rectangle x:Name="LineColorRect" Style="{StaticResource ColorSelectionRectangleStyle}" />
</Button.Content>
</Button>
</StackPanel>
Above is one example of stack panels I am using. In the code behind the function "Color_Click" will change this "ColorOption" stack panel state and do something.
However after I try to put this stack panel into Windows.Resources
<Window.Resources>
<StackPanel x:Name="ColorOption" Visibility="Collapsed" x:Key="ColorOption">
<TextBlock Text="Line Color" Style="{StaticResource ItemNameTextBlockStyle}"/>
<Button Style="{StaticResource ColorButtonStyle}" Click="Color_Click">
<Button.Content>
<Rectangle x:Name="LineColorRect" Style="{StaticResource ColorSelectionRectangleStyle}" />
</Button.Content>
</Button>
</StackPanel>
</Window.Resources> (I also put the style files inside)
In the tab controls I did
<TabControl>
<TabItem Header="Tab 1" Content="{StaticResource ColorOption}"/>
</TabControl>
The visual studio shows error in the code behind says "ColorOption does not exist in the current context"
How can I fix this? Is any way to set the context? thank you
You can simply wrap the StackPanel in ContentControl and make it ControlTemplate.
<ControlTemplate x:Key="ColorOptionTemplate" TargetType="{x:Type ContentControl}">
<StackPanel x:Name="ColorOption" Visibility="Collapsed">
<TextBlock Text="Line Color" Style="{StaticResource ItemNameTextBlockStyle}"/>
<Button Style="{StaticResource ColorButtonStyle}" Click="Color_Click">
<Button.Content>
<Rectangle x:Name="LineColorRect" Style="{StaticResource ColorSelectionRectangleStyle}"/>
</Button.Content>
</Button>
</StackPanel>
</ControlTemplate>
However, you will need to change properties of controls inside the ContentControl and it would be cumbersome. So the StackPanel could be wrapped in UserControl instead.
<UserControl x:Class="WpfApp1.ColorOptionControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel x:Name="ColorOption" Visibility="Collapsed">
<TextBlock Text="Line Color" Style="{StaticResource ItemNameTextBlockStyle}"/>
<Button Style="{StaticResource ColorButtonStyle}" Click="Color_Click">
<Button.Content>
<Rectangle x:Name="LineColorRect" Style="{StaticResource ColorSelectionRectangleStyle}"/>
</Button.Content>
</Button>
</StackPanel>
</UserControl>
This is a common way in WPF. The downside is that you will need to add dependency properties to UserControl and wire up them with dependency properties of internal controls so that you can set their values at the level of UserControl and bridge them with external controls and window. This could be complicated and cumbersome as well.
So I think ideally it would be better to find an existing control which has similar functionalities you want and create a custom control deriving from the existing one.

Centering several buttons WPF

I was writing C# code in winforms. In winForms using this menu (in the picture) I make center several buttons. But I can not find out any way to make center some elements horizontally in WPF.
Use the HorizontalAlignment property. For example:
<StackPanel Orientation="Horizontal" Height="50" HorizontalAlignment="Center">
<Button Width="50" Margin="5"></Button>
<Button Width="50" Margin="5"></Button>
<Button Width="50" Margin="5"></Button>
<Button Width="50" Margin="5"></Button>
</StackPanel>

Assign controls to a property in XAML

Hej all,
I wonder if it's possible to assign more than one control to a property in XAML.
Say I have 2 controls in my XAML:
<Button x:Name="_Btn1 Content="Button 1" />
<Button x:Name="_Btn2 Content="Button 2" />
<local:MyControl x:Name="_MyCtrl" Controls="{what goes here?}" />
Or should I declare my control as a container control and put all controls inside it, like so:
<local:MyControl x:Name="_MyCtrl">
<Button x:Name="_Btn1 Content="Button 1" />
<Button x:Name="_Btn2 Content="Button 2" />
</local:MyControl>
Thnx in advance!
Grtz,
Dwi
You can do that if you make MyControl inherit ItemsControl.
Then this
<local:MyControl x:Name="_MyCtrl">
<Button x:Name="_Btn1 Content="Button 1" />
<Button x:Name="_Btn2 Content="Button 2" />
</local:MyControl>
will work and this
<local:MyControl x:Name="_MyCtrl" ItemsSource="ViewModelorControlCollection" />
will work too.
The ViewModelorControlCollection can be defined in your ViewModel or as a static resource in your xaml.
It really depends what you're gonna do. Usually you'd simply define a DataTemplate instead.
<local:MyControl x:Name="_MyCtrl">
<local:MyControl.ControlsTemplate>
<DataTemplate>
<StackPanel>
<Button x:Name="btn1" Content="Button 1" />
<Button x:Name="btn2" Content="Button 2" />
</StackPanel>
</DataTemplate>
</local:MyControl.ControlsTemplate>
</local:MyControl>
You definitely need some kind of container. This can be a simple list or a specialized collection. Whatever you use in your control.
<local:MyControl>
<local:MyControl.Controls>
<ControlCollection> <!-- or whatever -->
<Button/>
<Button/>
</ControlCollection>
</local:MyControl.Controls>
</local:MyControl>
If you use a Panel or similar classes, you don't need to specify the ControlCollection explicitly. You then can define the controls like with e.g. the StackPanel.

How to dynamically create buttons in panorama items?

I have String array. It looks something like:
I have an empty panorama:
<controls:Panorama x:Name="MainPanorama">
</controls:Panorama>
I want to dynamically, using my array, create panorama page with panorama items,
it should be like:
<controls:Panorama x:Name="MainPanorama">
<controls:PanoramaItem Header="first">
<ScrollViewer>
<StackPanel>
<Button Name="button1" content="create"/>
<Button Name="button2" content="save"/>
<Button Name="button3" content="open"/>
</StackPanel>
</ScrollViewer>
</controls:PanoramaItem>
<controls:PanoramaItem Header="second">
<ScrollViewer>
<StackPanel>
<Button Name="button4" content="save as"/>
<Button Name="button5" content="import"/>
</StackPanel>
</ScrollViewer>
</controls:PanoramaItem>
</controls:Panorama>
So, the question is: how to dynamically create buttons in panorama items? Buttons must have different names.
p.s: The array that I show here is not the actual one. In my work I have another larger one, but I think it's enough to understand the problem.
You can use items controls for this goal (ListBox, or etc): bind a string array as item source and set buttons as item template. Please read more about controls and binding in Silverlight.

Aligning columns without WPF Grid

I am looking for a good way to design a multi-column layout which reflows the controls in the columns according to the space available. I have a list of labels and fields which display information, and sometimes the view they are contained in needs to be tall and skinny, other times short and wide.
A simple solution is to use a WrapPanel:
<WrapPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label>Some label:</Label>
<TextBlock>Some value</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label>Some other label:</Label>
<TextBlock>Some bigger value</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label>A:</Label>
<TextBlock>B</TextBlock>
</StackPanel>
</WrapPanel>
I want the labels and values all to line up horizontally into columns, without specifying a static width. Right now the Labels and TextBlocks are just sized based on their content.
Did you try to add WrapPanel as an ItemsContainer in ListBox?
<ListBox>
<ListBox.ItemsContainer>
<WrapPanel />
</ListBox.ItemsContainer>
</ListBox>

Resources