UserControl with a combobox and a listView - c#

I have a Usercontrol that contains a ListView, Add Button and a ComboBox. The intention is that when a user clicks Add the selected item in the ComboBox is added to the ListView.
This functionality works fine, however it would be great if the item in the ComboBox was removed list as duplicates aren't allowed in the ListView. Currently I am handling this in the ViewModel, but that means I have to implement the same code for each instance of the UserControl
Does anyone have a good idea how to implement a generic solution for this problem?
<UserControl>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListView x:Name="ListView"
ItemsSource="{x:Bind ListViewItemsSource}"
SelectedItem="{x:Bind ListViewSelectedItem, Mode=TwoWay}"
ItemTemplate="{x:Bind ListViewItemTemplate}"
Style="{x:Bind ListViewStyle}"
Margin="{StaticResource MediumBottomMargin}"
Visibility="{x:Bind ListViewVisibility, Mode=TwoWay}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<uwptoolkit:WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<StackPanel Grid.Row="1"
Orientation="Horizontal">
<ComboBox x:Name="ComboBox"
Style="{StaticResource StandardComboStyleFixedWidth}"
ItemsSource="{x:Bind ComboBoxItemsSource, Mode=OneWay}"
SelectedItem="{x:Bind ComboBoxSelectedItem, Mode=TwoWay}"
DisplayMemberPath="{x:Bind ComboBoxDisplayMemberPath}"
PlaceholderText="{x:Bind ComboBoxPlaceholderText}"
BorderBrush="{x:Bind ComboBoxBorderBrush, Mode=TwoWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ComboBox>
<Button x:Name="AddButton"
Style="{StaticResource AddButtonStyle}"
Click="AddButton_Click"
IsEnabled="{x:Bind AddButtonIsEnabled, Mode=TwoWay}"/>
</StackPanel>
</Grid>
</UserControl>
EDIT:
I should add that I didn't create this class, I have inherited it and am looking to improve it. I will implement the style changes suggested.
What I mean by code behind is this:
<uc:ListTagControl
ComboBoxItemsSource="{x:Bind _viewModel.TypeItems, Mode=TwoWay}"
ComboBoxSelectedItem="{x:Bind _viewModel.SafetyType, Mode=TwoWay}"
ComboBoxDisplayMemberPath="Type"
ListViewSelectedItem="{x:Bind _viewModel.SelectedListViewItem, Mode=TwoWay}"
ListViewItemsSource="{x:Bind _viewModel.Safeties, Mode=TwoWay}"
AddButtonOnClick="{x:Bind _viewModel.AddType}"
AddButtonIsEnabled="True" />
In the ViewModel it will then do this, to remove the item from the combobox:
TypeItems.Remove(TypeItems
.Where(i => i.Id == _Safety.SafetyType.Id).Single());
I want to move this as a generic implementation into the xaml.cs

Related

Using an ItemsControl inside of a ListView ItemTemplate with XAML

So, a bit of a crazy request. I need a list of items, which I plan to use a ListView for, but inside each element, I need another "List" so-to-speak that will expand horizontally.
Here is a crude drawing of the basic idea I'm going for:
For my code, this is what I've tried, but it's not working. (And yes, I'm purposely not using binding yet so I can just use the design view to get everything looking right)
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Grid.IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="KpiColumn0" />
<ColumnDefinition Width="Auto" SharedSizeGroup="KpiColumn1" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" Grid.Row="0" Grid.Column="0">
<TextBlock Text="Test" />
<TextBlock Text="Test" FontSize="8" Margin="0,-4,0,0" />
</StackPanel>
<TextBlock Grid.Row="0" Grid.Column="2" Text="Test" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Test" />
<TextBlock Grid.Row="1" Grid.Column="2" Text="Test" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Items>
<MenuItem />
</ListView.Items>
</ListView>
How can I use a ListView to hold a list of items in each of its item, that expand horizontally, based on how many elements are in the List that each ListItem is bound to?
Add this to your inner ItemsControl and the items will be shown in horizontal alignment.
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
Note that you won't be able to use the designer view for this. You'll need to remove the ListView.Items that you have, and instead use binding or code-behind to have it show when running.
Something like this should work, if you give the ListView a Name of LV:
LV.ItemsSource = new List<List<int>>
{
new List<int> {1,2,3,4, },
new List<int> {1,2,3,4, }
};

Applying The Same Height For All DataGridColumns when MaxWidth of the Header Is Set

I have an ItemsControl containing dynamically changable number of DataGrids:
<ItemsControl ItemsSource="{Binding Table.Columns}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplateSelector>
<local:ColumnTemplateSelector InputParameterColumnTemplate="{StaticResource InputParamterColumn}"
SingleParameterColumnTemplate="{StaticResource SingleParameterColumn}"/>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
The template for the "SingleParameterColumn" is defined like this:
<DataTemplate x:Key="SingleParameterColumn">
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Cells}"
RowHeight="25" RowHeaderWidth="0" >
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"
TextWrapping="Wrap"
TextAlignment="Center"
MaxWidth="60">
</TextBlock>
<Button>
<Image ... />
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplateSelector>....
</DataGridTemplateColumn.CellTemplateSelector>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
There is always one InputParameterColumn and at least one SingleParameterColumn. The InputParameterColumn has a fixed header name, whereas the header of the SingleParameterColumn can be arbitrarily long.
Since I do not want to have very wide columns I have defined the MaxWidth of the TextBlock in the header template to 60, which causes the header to be higher if the name of a column is very long.
This causes the columns to have different heights depending on the length of the header name.
Is there any way I can find out how tall is the tallest header in my ItemsControl and then set the same height for all the other headers so that my columns then all have the same size?
I could finally reproduce your problem, and managed to solve it with these changes:
Set Grid.IsSharedSizeScope="True" on the StackPanel that serves as ItemsPanel for your ItemsControl
On you ItemTemplate, Change the StackPanels of the HeaderTemplates for Grids
On each Grid, define a single row with the same SharedSizeGroup identifier
ItemsControl:
<ItemsControl ItemsSource="{Binding Table.Columns}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch"
Grid.IsSharedSizeScope="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplateSelector>
<local:ColumnTemplateSelector InputParameterColumnTemplate="{StaticResource InputParamterColumn}"
SingleParameterColumnTemplate="{StaticResource SingleParameterColumn}"/>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
ItemTemplate:
<DataTemplate x:Key="SingleParameterColumn">
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Cells}"
RowHeight="25" RowHeaderWidth="0">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"
SharedSizeGroup="DataGridHeaderRow" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}"
TextWrapping="Wrap"
TextAlignment="Center"
MaxWidth="60">
</TextBlock>
<Button Grid.Column="1">
<Image ... />
</Button>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplateSelector>....
</DataGridTemplateColumn.CellTemplateSelector>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
You could write a behavior that attaches to the header TextBlock, and does the following:
subscribe to the SizeChanged event
store somewhere (static prop or helper class...) the largest size
if this largest size changes, just change the height of all elements the behavior is attached to

Listview with multiple columns not acting properly when height of Item expanded

Hello I need a multicolumn control with variable heights and with expand/collapse capability. My approach so far is this-
<ListView Grid.Row="1" ItemsSource="{Binding MyCollection}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" Grid.IsSharedSizeScope="True" VerticalContentAlignment="Center">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel></WrapPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<control:UCNewsFeed Margin="6" DataContext="{Binding Post}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
please notice that UCNewsFeed is a usercontrol which is given below-
<Grid x:Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="57"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid x:Name="namePanel">
<StackPanel x:Name="nameContainer">
<TextBlock>Lamia Mehreen</TextBlock>
<TextBlock>Monday at 12:02pm</TextBlock>
<Button/>
</StackPanel>
</Grid>
<Border x:Name="hiddenPanel" Visibility="Collapsed" Grid.Row="1">
<StackPanel x:Name="editPanel" Orientation="Horizontal">
<RichTextBox/>
<Button Margin="0" Foreground="{x:Null}" Style="{DynamicResource ButtonStyle1}" BorderThickness="0" Background="{x:Null}" BorderBrush="{x:Null}" Focusable="False" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="0" Width="37" Height="37"/>
</StackPanel>
</Border>
</Grid>
The layout is so far okay. The problem arises when "hiddenPanel"'s visibility is switched to visible from code behind. The entire row of the listview gets the height of the selected row.
I need to expand one cell only, NOT an entire row. Please suggest any approach that might come in handy. (I have simplified the XAML for easy reading, they might not look like the attached images)

Spacing between ListItems on Horizontal Stackpanel

How do I control the amount of space between items coming from the Biding 'Shorthand'? At the moment I have gaps between which seem to be dependent on the size of the value of 'Shorthand' itself (so if the value is 1 character the gap between it and the next value is bigger compared to if the value is 2 characters long).
I have tried putting the margin and padding to zero in various places to no avail.
<ListView ItemsSource="{Binding Rounds}" IsItemClickEnabled="False" ItemClick="ItemView_ItemClick" ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<ListView.ItemTemplate>
<DataTemplate >
<StackPanel>
<TextBlock Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}">
<Run Text="Round "/>
<Run Text="{Binding RoundNumber}" />
</TextBlock>
<ListView ItemsSource="{Binding Formations}" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Shorthand}" Style="{ThemeResource ListViewItemTextBlockStyle}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I'm not sure where in the Template the margin/padding is set. As a workaround you may try to set a negative Margin to your ItemContainerStyle:
<ListView Name="myList">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0,0,0,-20" />
</Style>
</ListView.ItemContainerStyle>
// rest of the code
The first occurrence of ItemView of ListView is defined inline. So when ItemSource is set, the ItemTemplate is applied to EVERY item.
ListView.ItemTemplate encompassing Binding Shorthand. Place it in a Grid and define Grid.ColumnDefinitions to Width="Auto" or "*" for DataTemplate.
The StackPanel may help position it better. You will have to adjust Grid defs to your display requirements needed.
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Text="{Binding Shorthand}" Style="{ThemeResource ListViewItemTextBlockStyle}" />
</StackPanel>
</Grid>
</DataTemplate>

ListView with dynamic raws and columns that stays propetinal aytomatically

I am developing a WPF application.
have a list of user controls
<Grid >
<ListView ItemsSource="{Binding MyList}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType=ListView}}"
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType=ListView}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate >
<DataTemplate >
<Views:MyUserControl Height="auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SnapsToDevicePixels="True"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
And MyUserControl has another list of user controls "DetailUserControl":
<UserControl x:Class="MyUserControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="45" />
<RowDefinition />
</Grid.RowDefinitions>
<Border Grid.RowSpan="2" >
<StackPanel VerticalAlignment="Top" Orientation="Horizontal" HorizontalAlignment="Center" >
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10" Text="{Binding Title}" />
</StackPanel>
</Border>
<Border Grid.Row="1" Style="{StaticResource SummaryInnerFill}" >
<ItemsControl ItemsSource="{Binding DetailUserControls}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</Grid>
I used wrap panel to make all the usercontrols to be orderd from left to right one after the other.
I want the layout to be optimized and use as much as it can from the first line, and only then it will move to the second line.
here is an example (sorry for my drawing skills :) )
I have the following layout:
if I change the order inside the tiles I can have the following layout (that contains the same tiles but don't waste space an is much more organized):
Is there a panel I can use that will take care of it?
You can make use of Uniform Control Grid. You can specify Rows and Columns Properties. Controls are added to the grid in the order that they are declared.Its child controls are organised into a tabular structure of rows and columns. Unlike the Grid control, you don't have fine-grained control of the layout.
Example,
<UniformGrid Name="uniformGrid1"
Rows="2"
Columns="3">
<Button Content="blah"
Margin="2" />
<Button Content="blah1"
Margin="2" />
<Button Content="blah2"
Margin="2" />
</UniformGrid>
UniformGrid

Categories

Resources