I have the code below which I want to display a series of buttons horizontally with an image and text label, and below that row of buttons one label for the entire button group. What I currently have displays the buttons in a row, but the group label displays in the same row to the left of the buttons. I'm probably doing this wrong, but I'm impressed that I got this far. I've tried putting the Child ItemsPanel in a StackPanel, but then it shows no buttons - it seems to loose contact with the collection from the parent window. Is there an easy way to connect this collection and organize the label below the buttons? I'm thinking my next try is to collect the data from the parent in the child's code-behind and build an ItemsSource which I will then connect to the ItemTemplate, but I don't really know how to do that either.
Parent window snippet:
<User_Controls:ToolbarGroup GroupLabel="Group 1">
<User_Controls:ImageButton ButtonText="Test 1"/>
<User_Controls:ImageButton ButtonText="Test 2"/>
<User_Controls:ImageButton ButtonText="Test 3"/>
</User_Controls:ToolbarGroup>
Child window code:
<ItemsControl x:Class="Fiducia_WPF.User_Controls.ToolbarGroup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:User_Controls="clr-namespace:Fiducia_WPF.User_Controls"
mc:Ignorable="d"
Loaded="ItemsControl_Loaded"
d:DesignHeight="170" d:DesignWidth="320">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Height="170" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<User_Controls:ImageButton ButtonText="Temp"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<Label Name="lblGroupTitle" Grid.Row="1" Grid.Column="0" Content="Group X" FontSize="16" FontWeight="Bold" Margin="0,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Visible" />
</ItemsControl>
You should create a ControlTemplate for your ItemsControl:
<ItemsControl x:Class="Fiducia_WPF.User_Controls.ToolbarGroup" ...>
<ItemsControl.Template>
<ControlTemplate TargetType="ItemsControl">
<StackPanel>
<ItemsPresenter/>
<Label x:Name="lblGroupTitle" Content="Group X" ... />
</StackPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Height="170" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<User_Controls:ImageButton ButtonText="Temp"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Setting the Content of the lblGroupTitle Label may probably better be done by binding it to a property of the ToolbarGroup control:
<Label Content="{Binding GroupTitle,
RelativeSource={RelativeSource AncestorType=User_Controls:ToolbarGroup}}" ... />
I'd just use a straight up Grid:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0" ... />
<Button Grid.Row="0" Grid.Column="1" ... />
<Button Grid.Row="0" Grid.Column="2" ... />
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" />
</Grid>
Related
**Tried adding wrap panel as parent, as child, as item template parent, but didn't work
**
<UserControl.Resources>
<DataTemplate x:Key="CredentialTemplate" DataType="{x:Type local:Credentials}">
<WrapPanel Width="800" Orientation="Horizontal">
<Grid Background="Red" Width="160" Height="160">
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding FileName}" FontWeight="Bold" Margin="0,0,10,0"/>
<Button Grid.Row="1" Grid.Column="0" x:Name="btnCopyUsername" Tag="{Binding Button}" Content="Copy Username" Click="OnCopyUsernameButtonClicked" Margin="10"/>
<Button Grid.Row="1" Grid.Column="1" x:Name="btnCopyPassword" Tag="{Binding Button}" Content="Copy Password" Click="OnCopyPasswordButtonClicked" Margin="10"/>
<Button Grid.Row="2" Grid.Column="0" x:Name="btnEdit" Tag="{Binding Button}" Content="Edit" Click="OnEditButtonClicked" Margin="10"/>
<Button Grid.Row="2" Grid.Column="1" x:Name="btnCopyBoth" Tag="{Binding Button}" Content="Copy Both" Click="OnCopyBothButtonClicked" Margin="10"/>
</Grid>
</WrapPanel>
</DataTemplate>
</UserControl.Resources>
<WrapPanel Width="900" Background="Yellow">
<ListBox ItemsSource="{Binding Credentials}" ItemTemplate="{StaticResource CredentialTemplate}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Background="Green" Margin="10"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</WrapPanel>
</UserControl>
It might be the grid control that not letting wrap, normally without item template it should work
Screenshot
I explored this issue with a simplified version of your markup.
I don't have a credentials class, but what's in the grids doesn't matter.
My markup has less wrappanels:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="CredentialTemplate">
<Grid Background="Red" Width="40" Height="40">
</Grid>
</DataTemplate>
</Grid.Resources>
<ListBox ItemsSource="{Binding Points}"
ItemTemplate="{StaticResource CredentialTemplate}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"
Background="Green" Margin="10"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
I've got 30 Points in my viewmodel.
My grid is smaller
Wraps ok:
My MainWindow is built with TabContol containing in each tab UserControl in xaml files. Opening specific UserControl is not a problem, but aligning it is. I was able to horizontally center content of tab but struggle to vertically do this same. I found out that the root problem is that UserControl don't take the whole free space (height) in the Tab. I tried to make main grid VerticalAlignment="Stretch" and "Center" but that didn't help. I could use margin with specific number or define row fixed hight but that will not work on every resolution and I don't want to write method in code behind but use the power of xaml. How can I force UserControl to take whole height in Tab and then vertically center it (it's important to do it for specific UserControl because others should have default position)?
ps. I'm using MetroWindow from MahApps.Metro.
MainWindow main Grid:
<Grid>
<StackPanel>
<TabControl ItemsSource="{Binding Tabs}"
SelectedIndex="0">
<TabControl.Resources>
<Style TargetType="{x:Type TabPanel}">
<Setter Property="HorizontalAlignment"
Value="Center" />
</Style>
<DataTemplate DataType="{x:Type VMod:LoginViewModel}">
<Pages:LoginView />
</DataTemplate>
<DataTemplate DataType="{x:Type VMod:AdminViewModel}">
<Pages:AdminView />
</DataTemplate>
<DataTemplate DataType="{x:Type VMod:ProductsViewModel}">
<Pages:ProductsView />
</DataTemplate>
<DataTemplate DataType="{x:Type VMod:DistributionViewModel}">
<Pages:DistributionView />
</DataTemplate>
<DataTemplate DataType="{x:Type VMod:SummaryViewModel}">
<Pages:SummaryView />
</DataTemplate>
<DataTemplate DataType="{x:Type VMod:SettingsViewModel}">
<Pages:SettingsView />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type inter:ITab}">
<TextBlock>
<Run Text="{Binding TabName}" />
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</StackPanel>
</Grid>
UserControl main Grid:
<Grid Background="LightBlue"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Height="300"
Width="300"
Grid.Row="2"
BorderBrush="LightGray"
BorderThickness="1">
<StackPanel HorizontalAlignment="Center">
<iconPacks:PackIconRPGAwesome Kind="Honeycomb"
HorizontalAlignment="Center"
Width="60"
Height="60"
Margin="0, 0, 0, 0"/>
<TextBlock HorizontalAlignment="Center"
Text="DistributionTool"
FontSize="20"
FontWeight="Bold"
Margin="5" />
<Grid Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Margin="5"
TextAlignment="Left"
FontSize="15"/>
<iconPacks:PackIconMaterial Grid.Column="1"
Kind="AccountTie"
Width="20"
Height="20"
VerticalAlignment="Center"/>
</Grid>
<Grid Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<PasswordBox Grid.Column="0"
Margin="5"
HorizontalContentAlignment="Left"
FontSize="15"
Style="{StaticResource Win8MetroPasswordBox}" />
<iconPacks:PackIconMaterial Grid.Column="1"
Kind="Key"
Width="20"
Height="20"
VerticalAlignment="Center" />
</Grid>
<Button Content="LOGIN"
Width="80"
metro:ControlsHelper.ContentCharacterCasing="Normal"
Margin="5"
Style="{StaticResource AccentedSquareButtonStyle}" />
</StackPanel>
</Border>
</Grid>
From what I gather, what you could try would be:
Remove the StackPanel in your MainWindow Grid. Unless you intend to have more than 1 child inside the stack panel (Other than your TabControl), it is useless.
Add VerticalAlignement="Stretch" to your TabControl. This will allow it to take up all the space it can vertically.
Then you should be pretty much set to go.
The reason why you shouldn't use a StackPanel unless you intend to stack items inside, as in
<StackPanel>
<Child1/>
<Child2/>
</StackPanel>
is that the StackPanel.Orientation property affects how things will appear inside, including the Alignement of each child.
So Orientation="Vertical" (the default), affects the VerticalAlignement of its children. Same idea with Horizontal.
I have a Grid defined as such
<Grid DockPanel.Dock="Top" x:Name="grdFilter" Margin="40 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.ColumnSpan="3" Grid.Row="5" Orientation="Horizontal"
Margin="5" HorizontalAlignment="Center">
<Button Content="Apply"
Margin="0 0 2 0"
Style="{StaticResource MyButtonStyle}"
Command="{Binding ApplyFilterCommand}"/>
<Button x:Name="btnCancelFilter"
Margin="2 0 0 0"
Content="Cancel"
Style="{StaticResource MyButtonStyle}"
Click="btnCancelFilter_Click" />
</StackPanel>
</Grid>
And an ObservableCollection<string> in my ViewModel that defines a list of strings to filter some data on. I want to present each item in the ObservableCollection in a separate cell in the Grid defined above using a checkbox and a label. I realize there is a finite number of possible cells; I'm not worried about that right now.
If I hard-code the items, I'm stuck repeating the definitions for the checkbox and label for every cell of the grid
<DockPanel Grid.Column="0" Grid.Row="0">
<CheckBox x:Name="filterCheckbox"
Command="SomeFilterCommand">
<CheckBox.LayoutTransform>
<ScaleTransform ScaleX="3" ScaleY="3"/>
</CheckBox.LayoutTransform>
</CheckBox>
<Label Content="Filter 1" />
</DockPanel>
<DockPanel Grid.Column="1" Grid.Row="0">
<CheckBox x:Name="filterCheckbox"
Command="SomeFilterCommand">
<CheckBox.LayoutTransform>
<ScaleTransform ScaleX="3" ScaleY="3"/>
</CheckBox.LayoutTransform>
</CheckBox>
<Label Content="Filter 2" />
</DockPanel>
etc... I thought maybe I could create a DataTemplate and somehow bash that into the Grid with some voodoo but I don't know how to do it or if it's even possible.
Here's my DataTemplate
<DataTemplate x:Key="FilterTemplate">
<DockPanel>
<CheckBox x:Name="filterCheckbox"
Tag="{Binding Mode=OneWay}"
Command="SomeFilterCommandFromBindings">
<CheckBox.LayoutTransform>
<ScaleTransform ScaleX="3" ScaleY="3"/>
</CheckBox.LayoutTransform>
</CheckBox>
<Label Content="{Binding Mode=OneWay}" />
</DockPanel>
</DataTemplate>
Is there any other way to load a collection of items into a grid cell-by-cell with a template? Or another control that I can use?
A grid can't be used as an ItemsPanel Template but a Uniform Grid can, which is not as flexible but usually does the job in these kinds of situations
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="3"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DockPanel>
<!-- Template here -->
</DockPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You can also use a ListBox or ListView, depending on the functionality required; ItemsControl doesn't have a scrollviewer or SelectedItem for example, but if you just need to display data then ItemxControl is the way to go.
I have a TabContorl and I am adding tabitem dynamically. I have bind datatemplate to some viewmodel and view is loaded as defined in datatemplate.
<DataTemplate DataType="{x:Type viewmodels:ItemViewModel}">
<views:CustomerOrderView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:InvoiceGeneratorViewModel}">
<views:InvoiceGenerator/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:OrderDetailsViewModel}">
<views:OrderDetailsView/>
</DataTemplate>
if i use controls directly in customerorderview then it works fine. It means if i switch the tab all values are loaded accordingly but does not work properly when if i use another usercontrol insite customerview user control. I have created once user control have one textbox, button and a popup contorl. When i click on button the popup opens and remains even if swich the tab. If textbox value is change the value is also changed for all tabs. It looks one view is serving all the tabs. I could not understand how to handle it.
Below is the user control which i am using inside customer
<UserControl x:Class="OrderManagement.Views.ProductSelectionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="txtProduct" Grid.Row="0" Grid.Column="0"/>
<Button x:Name="btnSearch" Content="S" Click="OnSearchClick" Grid.Row="0" Grid.Column="1" Margin="5"/>
<Popup x:Name="popup" PlacementTarget="{Binding ElementName=btnSearch}" Placement="Bottom"
Width="300" Height="300" Margin="5">
<Border BorderBrush="Black" BorderThickness="2" Background="AliceBlue">
<Grid >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="35"/>
</Grid.RowDefinitions>
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False" Grid.Row="0" Grid.ColumnSpan="2"
IsReadOnly="False" CanUserAddRows="False" CanUserDeleteRows="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding IsChecked}" IsReadOnly="False"/>
<DataGridTextColumn Header="Product Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Quantity In Stock" Binding="{Binding QuantityInStock}"/>
</DataGrid.Columns>
</DataGrid>
<WrapPanel Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Right">
<Button Content="OK" Click="OnOKClick" Width="80"/>
<Button Content="Cancel" Click="OnCancelClick" Width="80" Margin="20,0,0,0"/>
</WrapPanel>
</Grid>
</Border>
</Popup>
</Grid>
</UserControl>
Tabcontrol virtualize the data as per its datacontext but popup and text value in productselection control remains the same for all tabs. opens the popup but it remains for all the tab.
Let me know if how it can be done or what is the mistake i am doing
I am facing issue while arranging Stack panel as mention in the screenshots.
I am new to the WPF and I need to design a layout that look something like as mention in the image.
Zahorak is correct. There are a number of ways to create this layout, the best being a grid or a DockPanel. I prefer a DockPanel. A DockPanel allows you to position child controls around the edge of the DockPanel, filling the rest of the DockPanel (if you don't specify otherwise) with the last child control. For example, to achieve the result you are after, the xaml would be
<DockPanel>
<StackPanel DockPanel.Dock="Top">
<TextBlock Text="Stack Panel 2" />
</StackPanel>
<StackPanel DockPanel.Dock="Left">
<TextBlock Text="Stack Panel 3" />
</StackPanel>
<StackPanel DockPanel.Dock="Bottom">
<TextBlock Text="Stack Panel 5" />
</StackPanel>
<StackPanel>
<TextBlock Text="Stack Panel 4" />
</StackPanel>
</DockPanel>
A Grid layout requires a little more work.
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.ColumnSpan="2">
<TextBlock Text="Stack Panel 2" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.RowSpan="2">
<TextBlock Text="Stack Panel 3" />
</StackPanel>
<StackPanel Grid.Row="1" Grid.Column="1">
<TextBlock Text="Stack Panel 4" />
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="1">
<TextBlock Text="Stack Panel 5" />
</StackPanel>
</Grid>
Note that a missing Grid.Row or Grid.Column means a value of 0 (the first row or first column).
I hope this helps.
Here is a way with pure stackpanels. You would have to set a width for the tall, narrow one (Stackpanel 3) in the second row
<StackPanel Orientation="Vertical" Name="StackPanel1">
<StackPanel Orientation="Horizontal" Name="StackPanel2">
<StackPanel>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical" Name="StackPanel3">
<StackPanel>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" Name="StackPanel4">
<StackPanel>
<StackPanel Orientation="Horizontal" Name="StackPanel5">
<StackPanel>
<StackPanel>
<StackPanel>
<StackPanel>