WPF ComboBox: Align selected value and/or drop down items - c#

I have an almost default WPF ComboBox:
What is the simplest way to align items, for instance, to the right in combobox's input and/or drop down?
I've looked around and found only solutions messing with the control templates which in my opinion is too long shot for such a simple thing. I can't believe there is no simpler solution we can do for aligning the items.
UPDATE:
I've slightly reformulated the question to cover a broader set of cases further readers might experience on this topic as it turned out thanks to dkozl's answer.
Also, it should prevent some people from trying to close this question as a duplicate.

If you want to right align both selected value and drop down items then setting HorizontalContentAlignment="Right" against ComboBox should be enough
<ComboBox ... HorizontalContentAlignment="Right">
if you want to right align only drop down items then you need to change HorizontalContentAlignment of the ComboBoxItem
<ComboBox>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
and to right align only selected value combination of both
<ComboBox ... HorizontalContentAlignment="Right">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>

I've tried both things from dkozl's answer and by some reason setting HorizontalContentAlignment against my ComboBox was sufficient to align to the right items in both combobox's input and drop down.

I had the problem of aligning the selected item on the right, so that users can always see the end of the string.
Simplest solution I found was the following
<ComboBox ...>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock HorizontalAlignment="Right" Text="{Binding textProp}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Related

WPF Datagrid Grouping and totals [duplicate]

I'm implementing a WPF DataGrid that contains projects with many key figures. Projects are grouped by project categories.
For each category there should be:
a row that shows in each key figure column sum of all rows for the column.
a target row that is not part of the datasource grid in binded to. target row tells for every column what is target for the year (e.g. how much money there's to spend).
These rows should be always on top in each group (sorting filtering).
My 1st solution was to have this data in group header. This is not a good solution because group header does not support columns. i.e. it should be constructed by getting column widths.
That could be done but it gets complicated when users want to reorder and hide columns.
DataGrid is using CollectionViewSource so it's not populated with C# code. Basically i'm extending this example: http://msdn.microsoft.com/en-us/library/ff407126.aspx
Thanks & Best Regards - matti
I have a hacked-together DataGrid with group subtotal rows in one of my projects. We weren't concerned about some of the issues you bring up, such as hiding and sorting columns so I don't know for sure if it can be extended for that. I also realize there could be performance issues that may be a problem with large sets (my window is operating 32 separate DataGrids - ouch). But it's a different direction from other solutions I've seen, so I thought I'd throw it up here and see if it helps you out.
My solution consists of 2 major components:
1. The subtotal rows aren't rows in the main DataGrid, but are separate DataGrids. I have 2 extra grids in each group actually: 1 in the header that is only displayed when the group is collapsed, and one beneath the ItemsPresenter. The ItemsSource for the subtotal DataGrids comes from a Converter that takes the items in the group and returns an aggregate view model. The columns of the subtotal grids are exactly the same as the main grid (filled out in DataGrid_Loaded, though I'm sure it could be done in xaml too).
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Background="Gray" HorizontalAlignment="Left" IsExpanded="True"
ScrollViewer.CanContentScroll="True">
<Expander.Header>
<DataGrid Name="HeaderGrid" ItemsSource="{Binding Path=., Converter={StaticResource SumConverter}}"
Loaded="DataGrid_Loaded" HeadersVisibility="Row"
Margin="25 0 0 0" PreviewMouseDown="HeaderGrid_PreviewMouseDown">
<DataGrid.Style>
<Style TargetType="DataGrid">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Expander}, Path=IsExpanded}"
Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
</DataGrid>
</Expander.Header>
<StackPanel>
<ItemsPresenter/>
<DataGrid Name="FooterGrid" ItemsSource="{Binding ElementName=HeaderGrid, Path=ItemsSource, Mode=OneWay}"
Loaded="DataGrid_Loaded" HeadersVisibility="Row"
Margin="50 0 0 0">
<DataGrid.Style>
<Style TargetType="DataGrid">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Expander}, Path=IsExpanded}"
Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid>
</StackPanel>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
2. Then the issue is how to get all DataGrids to behave as if they were a single grid. I've handled that by subclassing DataGridTextColumn (we only have text in this case, but other column types should work too) in a class called DataGridSharedSizeTextColumn that mimics the SharedSizeGroup behavior of the Grid class. It has a string dependency property with a group name and keeps track of all columns in the same group. When Width.DesiredValue changes in one column, I update the MinWidth in all the other columns and force an update with DataGridOwner.UpdateLayout(). This class is also covering column reordering and does a group-wide update whenever DisplayIndex changes. I would think this method would also work with any other column property as long as it has a setter.
There were other annoying things to work out with selection, copying, etc. But it turned out to be pretty easy to handle with MouseEntered and MouseLeave events and by using a custom Copy command.
One option could be to add the rows in data source with special values for Name and other fields that do not make sense and use DataTrigger to show them with special colors and maybe some other.
Filtering is done in C# anyway so it doesn't affect these rows.
Sorting is the only problem here. It would be so cool just to tell that some rows are always with order 0 and with order 1 in the group. But cause i dunno how to do it I gotta make custom sorting in C# for all the columns instead of just declaring the sorting:
<CollectionViewSource.SortDescriptions>
<!-- Requires 'xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"' declaration. -->
<scm:SortDescription PropertyName="ProjectName"/>
<scm:SortDescription PropertyName="Complete" />
<scm:SortDescription PropertyName="DueDate" />
</CollectionViewSource.SortDescriptions>
EDIT: On top of everything else it has a major drawback comparing to my 1st solution (sum info in group header) because when filtering changes I should update the sums to be calculated only for visible rows.
So this answer is a complete hack and lacks all the elegance and uses no nice features that WPF is suppose to have :(

WPF Style can't work on multiple controls

I am using wpf style on my controls so that i can use an style on multiple controls at once. It usually works. for example i made an saveButtonStyle and i apply it on every save button on my application. But it doesn't work on MenuItems.
I made a style for my menuitems which contains an icon next to the items.
This is one screen shot of it.
You see the Datagrid has an ContextMenu and within it there is multiple menu items. in this case pay attention to Set Alarm. it has an icon. This Set Alarm Menu item is also in another menu datagrid next to this one. When i click that one
it appears too
But problem is when i right click back to the other datagrid the icon is gone and wont come back. this is the screen shot
Here is the style I made
<Style x:Key="menuItemAlert" TargetType="{x:Type MenuItem}">
<Setter Property="Icon">
<Setter.Value>
<Image Source="Content/AlertIcon.png" Width="20" Height="20" />
</Setter.Value>
</Setter>
</Style>
And here is how I apply it to my controls
<MenuItem x:Name="customerContextMenuSetAlarm" Header="SetAlarm" Style="{StaticResource menuItemAlert}" Click="customerContextMenuSetAlarm_Click"/>
Do you know why it happens?
style menuItemAlert creates only one instance of Image and can display it in one place only. to overcome this make a separate non-shared resource for that Image.
<Image x:Key="AlertIcon" x:Shared="False" Source="Content/AlertIcon.png" Width="20" Height="20" />
<Style x:Key="menuItemAlert" TargetType="{x:Type MenuItem}">
<Setter Property="Icon" Value="{StaticResource AlertIcon}"/>
</Style>

Binding color in combobox from code wpf

First of all sorry about my English. I'm generating a datagrid with columns and rows dynamically. Every column I generate this way:
FrameworkElementFactory frameElementFactory =
new FrameworkElementFactory(typeof(ComboBox));
itemsSourceBinding.Source = finalList;
frameElementFactory.SetBinding(ComboBox.ItemsSourceProperty, itemsSourceBinding);
I have a property in the items of finalLsit that has the hexa code of a color. I need to set the background of an item in the combobox with some color depending of that code.
EDIT: I need to do it from code, like setting a binding to the frameElementFactory. I can't do it in the XAML because it is dynamically, maybe I have to create 3 columns and only one with this binding, so I must do it programatically.
Use DataTemplate: You design a template to display your items inside a combobox. For example you design a textlabel to display the color and docked at left of the dropdown menu. You should also have a converter ready to covert color (IValueConverter).
<DataTemplate DataType="{x:Type ComboBoxItem}">
<DockPanel>
<TextBlock Background="{Binding HexaColor}" Width="30" DockPanel.Dock="Left" />
.....
</DockPanel>
</DataTemplate>
Or, you just set the resource to the combobox:
<ComboBox ItemsSource="{Binding finalList}">
<ComboBox.Resources>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Background" Value="{Binding ....}"/>
</Style>
</ComboBox.Resources>
</ComboBox>
Hope this helps

Can binding be used in XAML within a Style?

I wrote a sample to see if binding could be used within a Style in a blank Windows Store app - it compiled but did not work exactly as I'd hoped. I'm relatively new to XAML and binding so may have missed something.
In the sample below there are two rectangles, both bound to the slider control and both should change at the same time as the slider is moved, but it seems that only the first one changes; the first one is bound directly, the second is bound via a style.
Is binding in a Style supposed to be possible in a Win Store app?
(My aim is to have a slider that changes the settings on a large number of elements at once, it seemed like this would be a better approach than copy/pasting bindings to all of them)
<Grid Background="#FF87873D">
<StackPanel>
<StackPanel.Resources>
<Style x:Key="myTestRectangleStyle" TargetType="Rectangle">
<Setter Property="Fill" Value="DarkBlue" />
<Setter Property="Margin" Value="10,10" />
<Setter Property="Height" Value="30" />
<Setter Property="Width" Value="{Binding ElementName=slider1, Path=Value}" />
</Style>
</StackPanel.Resources>
<Rectangle Width="{Binding ElementName=slider1, Path=Value}" Fill="Black" Margin="10,10" Height="30"/>
<Rectangle Style="{StaticResource myTestRectangleStyle}"/>
<Slider Name="slider1" Minimum="20" Maximum="200" Margin="20,0"/>
</StackPanel>
</Grid>
Answering my own question...it seems this isn't possible on a Windows Store App.
I had a clarification from a user on a MSDN forum that
[Bindings] are not supported on Style setters in Windows Store Apps like
they are in WPF, i.e. you cannot bind to the Value property of the
Slider in the Style
So the workaround is just to set the binding directly outside of the Style (a long winded option if you have a lot of elements to bind unfortunately)

Better performance from datagrid grouping

I have a DataGrid which I am using with an ICollectionView to group items in a large collection, 20k+ rows in some instances. I have used this approach before with varying success with showing all rows or virtualizing to create a more responsive page. In this instance I would like to virtualize as much as possible to keep the UI responsive. I have used the tips in this answer with little success to my issues.
Helpful DataGrid Link
My main issue is a couple second lag on the DataGrid when loading the data into the ICollectionView View/Source, which I would like to minimize with the proper virtualization. Here is some of my code:
<DataGrid Margin="0,2,0,0" IsReadOnly="True" ItemsSource="{Binding DataView,IsAsync =True}" EnableRowVirtualization ="True" MaxWidth="2560" MaxHeight="1600"
Grid.Row="2" SelectionMode="Extended" VirtualizingPanel.IsVirtualizingWhenGrouping="True" SelectionUnit="FullRow" SelectedItem="{Binding SelectedOutage}">
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" Foreground="{StaticResource Foreground}" Background="{StaticResource AlternatingBackground}">
<Expander.Header>
<TextBlock Text="{Binding Name}" Margin="5,0,0,0" Width="300"/>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
<!--Follows are DataGrid.ContextMenu and DataGridTextColumns with fixed widths-->
</DataGrid>
And c# items:
public ICollectionView DataView { get; set; }
private readonly ObservableCollection<EquipmentMonitorRow> equipment = new ObservableCollection<EquipmentMonitorRow>();
DataView = CollectionViewSource.GetDefaultView(equipment);
DataView.GroupDescriptions.Add(new PropertyGroupDescription("GroupName"));
equipment.Clear();
//Lag is during this item adding.
equipment.AddRange(data);
So hopefully I'm missing some virtualization or maybe I can add the items differently or something.
Any help would be appreciated. Thanks.
Changing my ObservableCollection source to a List source solved the initial load lag.
private readonly List<EquipmentMonitorRow> equipment = new List<EquipmentMonitorRow>();
Also using a combination of VirtualizingPanel properties I achieved the best virtualization. Specifically if I omitted VirtualizingPanel.IsVirtualizingWhenGrouping="True" then the application lagged for almost a minute before showing anything in my DataGrid. The scrolling was a lot better without virtualization but the initial load was unacceptable in my case.
VirtualizingPanel.IsVirtualizingWhenGrouping="True" VirtualizingPanel.ScrollUnit ="Item" VirtualizingPanel.VirtualizationMode="Recycling"
Thanks for the help.
Datagrid by default support UI virtualization but as soon as you apply grouping on ICollectionView, UI virtualization will be turned off.
You can refer to MSDN sample which basically flattens the grouped list which supports virtualization.
UPDATE for comment:
This doesn't specifically mention a collectionview but seems there is
virtualization
From the link:
In a normal WPF ItemsControl that virtualizes, such as ListBox or
ListView, virtualization turns off when you turn grouping on.
And DataGrid derives from ItemsControl only, hence UI virtualization is turned off on grouping.

Categories