I'm trying to take into use a SelectButton (https://gist.github.com/loraderon/580405) but I need to specify MinWidth for it. Otherwise it's width is just the width of Extender. Removing ColumnSpan or setting 1st column Auto are not doing the trick. I would really like it to always have width of most wide element in list + extender symbol.
<UserControl x:Class="loraderon.Controls.SelectButton"
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:my="clr-namespace:loraderon.Controls"
mc:Ignorable="d"
SizeChanged="UserControl_SizeChanged"
d:DesignHeight="30" d:DesignWidth="100">
<Grid
x:Name="SplitGrid"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="23" />
</Grid.ColumnDefinitions>
<Button
x:Name="Button"
Click="Button_Click"
Grid.ColumnSpan="2"
Padding="0"
HorizontalContentAlignment="Left"
>
<ContentControl
x:Name="ButtonContent"
HorizontalContentAlignment="Center"
ContentTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemTemplate}"
/>
</Button>
<Expander
x:Name="Expander"
Expanded="Expander_Expanded"
Collapsed="Expander_Collapsed"
Grid.Column="1"
VerticalAlignment="Center"
IsExpanded="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=IsExpanded}"
/>
<Popup
IsOpen="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=IsExpanded}"
PlacementTarget="{Binding ElementName=Button}"
PopupAnimation="Fade"
StaysOpen="False"
>
<ListBox
x:Name="ListBox"
SelectionMode="Single"
SelectionChanged="ListBox_SelectionChanged"
SelectedIndex="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=SelectedIndex, Mode=TwoWay}"
ItemTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemTemplate}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemsSource}"
/>
</Popup>
</Grid>
</UserControl
EDIT: The window I placed the control had:
SizeToContent="WidthAndHeight"
which resulted both answers below not to work. Is there more robust solution that would work when placing the button in variety of controls/containers? It seems that the way the control was built is not very robust. Popup not being the part of visual tree makes it a bad choice.
The easy part is binding to the ListBox' ActualWidth
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ElementName=ListBox, Path=ActualWidth}"/>
<ColumnDefinition Width="23" />
</Grid.ColumnDefinitions>
The tricky part is that since the ListBox is located in a Popup, with it's own visual tree (Remarks), it only gets rendered when IsOpen is set to true.
The workaround is a swift open / close when the Control is loaded
public SelectButton()
{
InitializeComponent();
Loaded += (o, e) => Initialize();
}
void Initialize()
{
IsExpanded = true;
IsExpanded = false;
}
and an updated Expander_Expanded Method
private DateTime startUpTime = DateTime.Now;
private DateTime collapsedAt = DateTime.MinValue;
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
if (DateTime.Now - startUpTime <= TimeSpan.FromMilliseconds(200))
{
IsExpanded = true;
return;
}
if (DateTime.Now - collapsedAt <= TimeSpan.FromMilliseconds(200))
{
Expander.IsExpanded = false;
IsExpanded = false;
return;
}
IsExpanded = true;
}
EDIT
Turns out the TimeSpan of 200ms can be too small dependent on the system used, added a more robust solution
private bool startUp = true;
private DateTime collapsedAt = DateTime.MinValue;
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
if (startUp)
{
IsExpanded = true;
startUp = false;
return;
}
if (DateTime.Now - collapsedAt <= TimeSpan.FromMilliseconds(200))
{
Expander.IsExpanded = false;
IsExpanded = false;
return;
}
IsExpanded = true;
}
This is not pretty, but working. Since you already do Code-Behind, this might fit your needs:
First, the ItemsSourceProperty
. Change it to:
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(SelectButton), new PropertyMetadata(ItemsSourceChanged ));
Second, prepare Constructor:
public SelectButton() {
InitializeComponent();
this.ListBox.Loaded += this.ListBoxOnLoaded;
}
Third, implement ItemnsSourceChanged
-Method:
private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
var self = d as SelectButton;
self.ListBoxOnLoaded(self.ListBox, new RoutedEventArgs());
}
Fourth, do the magic:
private void ListBoxOnLoaded(object sender, RoutedEventArgs routedEventArgs) {
var lb = sender as ListBox;
lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
this.col1.MinWidth = lb.DesiredSize.Width;
}
Last but not least, edit XAML:
<Grid x:Name="SplitGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" Name="col1" />
<ColumnDefinition Width="23" />
</Grid.ColumnDefinitions>
When the listbox has loaded, we simply do the measuring by ourself and apply the desired size to the first column.
Hope it helps :)
This is horrible answer but might give somebody an idea. I create an invisible Listbox to same location where the button content is and bind Grid.Column="0" MinWidth to it's ActualWidth.
Somehow this is a bit too wide. The width of the ListBox is too wide to assign to Grid.Column="0". The items in the popuplistbox are a lot more narrow. Max of these should be the width assigned to Grid.Column="0".
I also tried to have a buttton there and created additional dependencyproperty for its content. That was best looking (size was perfect) but then you would have to know preferably all the items and their sizes in different languages or at least one item. This is of course huge disadvantage.
EDIT: If this same could be achieved with ContentControl/ContentPresenter somehow to avoid 2 ListBox this would be far better.
EDIT2: This does not work. The Width is width of the 1st element so order or ItemsSource is relevant.
Here is the xaml:
<UserControl x:Class="loraderon.Controls.SelectButton"
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:my="clr-namespace:loraderon.Controls"
mc:Ignorable="d"
SizeChanged="UserControl_SizeChanged"
d:DesignHeight="30" d:DesignWidth="100">
<Grid
x:Name="SplitGrid"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="{Binding ActualWidth, ElementName=ContentListBox}"/>
<ColumnDefinition Width="23" />
</Grid.ColumnDefinitions>
<Button
x:Name="Button"
Click="Button_Click"
Grid.ColumnSpan="2"
Padding="0"
HorizontalContentAlignment="Left"
>
<ContentControl
x:Name="ButtonContent"
HorizontalContentAlignment="Center"
ContentTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemTemplate}"
/>
</Button>
<ListBox
Grid.Column="0"
x:Name="ContentListBox"
Visibility="Hidden"
MaxHeight="{Binding ActualHeight, ElementName=Button}"
HorizontalAlignment="Stretch"
ItemTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemTemplate}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemsSource}"/>
<Expander
x:Name="Expander"
Expanded="Expander_Expanded"
Collapsed="Expander_Collapsed"
Grid.Column="1"
VerticalAlignment="Center"
IsExpanded="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=IsExpanded}"
/>
<Popup
IsOpen="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=IsExpanded}"
PlacementTarget="{Binding ElementName=Button}"
PopupAnimation="Fade"
StaysOpen="False"
>
<ListBox
x:Name="ListBox"
SelectionMode="Single"
SelectionChanged="ListBox_SelectionChanged"
SelectedIndex="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=SelectedIndex, Mode=TwoWay}"
ItemTemplate="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemTemplate}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type my:SelectButton}}, Path=ItemsSource}"
/>
</Popup>
</Grid>
</UserControl
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With