I want to continuous display of buttons in wpf - wpf

In my application I have 4 button. If user will click on a button, that will disappear and remaining 3 still display.
The problem is that I don't want a control (button) gap between button.
I want remaining 3 should rearrange itself.
Which control I should use or how I can implement that. I am using MVVM in my application.

Button and use Visibility = Collapsed

There are multiple ways to achieve what you need. You have to be specific with you question. My way of doing this would be
xaml:
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button">
<EventSetter Event="Click" Handler="Button_Click"/>
</Style>
</StackPanel.Resources>
<Button Content="I am the first buttom" Margin="5"/>
<Button Content="I am the second button" Margin="5"/>
<Button Content="I am the third button" Margin="5"/>
</StackPanel >
CodeBehind:
public void Button_Click(object sender, EventArgs e)
{
(sender as Button).Visibility = Visibility.Collapsed;
}

Summary of the Solution: I would use a Grid layout with automatically expanding ColumnDefinitions (the default setting of Width="1*". When the button is clicked and disappears (Use Opacity="0"), you should change the width of the corresponding ColumnDefinition to 0.
Here are the before and after screen shots of what you want:
Before
After Click Donkey Button
After Click Bird Button
After Click Moose Button
After Click Giraffe Button(I accidentally resized before I screenshotted)
I know that you are using MVVM, but this problem is entirely in the user interface, so its encouraged to use code-behind for this solution. I don't recommend putting this type of code into mvvm.
For this solution, use a grid:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication4"
mc:Ignorable="d"
Title="Used for question 36258073" >
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<EventSetter Event="Click" Handler="AllButton_Click"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Name="ColumnDefinitionNull"/>
<ColumnDefinition Name="ColumnDefinitionOne"/>
<ColumnDefinition Name="ColumnDefinitionTwo"/>
<ColumnDefinition Name="ColumnDefinitionThree"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Button Name="ButtonNull" Content="Test1 Giraffe" Grid.Row="0" Grid.Column="0" />
<Button Name="ButtonOne" Content="Test2 Bird" Grid.Row="0" Grid.Column="1" />
<Button Name="ButtonTwo" Content="Test3 Donkey" Grid.Row="0" Grid.Column="2" />
<Button Name="ButtonThree" Content="Test4 Moose" Grid.Row="0" Grid.Column="3" />
</Grid>
and the code-behind for the button click is:
private void AllButton_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
button.Opacity = 0;
var zeroWidth = new GridLength(0.0);
switch (button.Name)
{
case "ButtonNull":
ColumnDefinitionNull.Width = zeroWidth;
break;
case "ButtonOne":
ColumnDefinitionOne.Width = zeroWidth;
break;
case "ButtonTwo":
ColumnDefinitionTwo.Width = zeroWidth;
break;
default:
ColumnDefinitionThree.Width = zeroWidth;
break;
}
}
The idea for the Opacity 0 came from another Stack Overflow post here. The notion about the buttons stretching in place of the other button disappearring comes from button layout and grid definition ideas this Stack Overflow post1, and this Stack Overflow post2, and this Stack Overflow post3 and this last related Stack Overflow post4.

Related

How do I create a custom pushpin with image, label, and click event?

I am working on a location aware app, where I want to have custom pushpins that have an image, and when you tap the image, a label is added. I have tried a couple of solutions...
I started with this code, from this article: http://igrali.wordpress.com/2011/12/06/making-a-custom-windows-phone-bing-pushpin-from-an-image/
<ControlTemplate
x:Key="PushpinMe"
TargetType="maps:Pushpin">
<Grid
Name="PushpinMeGrid"
Height="50"
Width="50"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Image
x:Name="PushpinMeImage"
Height="50"
Width="50"
Source="Pushpins/pushpinSeaplane.png" />
<TextBlock Text="{Binding Source=}"
</Grid>
</ControlTemplate>
Then I tried wrapping the image in a button, but that just made the pushpin essentially invisible. Then I tried using a control template from one of my prior apps, and modified it, and came up with this:
<Button
Name="PushpinButton"
Click="Button_Click">
<Button.Style>
<Style
TargetType="Button">
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="Button">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="*" />
<ColumnDefinition
Width="*" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Grid.Row="0"
Grid.RowSpan="2"
Height="50"
Width="50"
Source="Pushpins/pushpinSeaplane.png" />
<Grid
Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition
Height="39" />
<RowDefinition
Height="*" />
</Grid.RowDefinitions>
<Grid
Grid.Row="0"
Background="Black">
<TextBlock
Grid.Row="0"
Foreground="White"
Text="{Binding ElementName=me,
Path=Content}"
TextWrapping="Wrap"
Margin="5" />
</Grid>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
</ControlTemplate>
Still not a winner - I can't bind the content of the button, and therefore the textblock.
There will be a series of pushpins, with different images, and different labels, so ideally, I would like to come up with a template that I can use, and bind the image and the label from code. The code for the button's click event would be as simple as making the textblock visible or collapsed.
I know my second example is pretty ugly, but I was trying to make the visual look right - I'll modify it as needed for the visuals, but for the moment, I need to figure out how I can bind the image and the text from code. The button click event works with just a messagebox for now (to show that it registered the click event).
Thanks for your assistance.
Sounds like a fun project! I've implemented databinding on nested content controls within a button before using declarations similar to the following. So in your case the push pin collection would be bound to the items control with each push pin object providing the data for its corresponding button (including the button's nested image and textblock).
Let's take a look at a simple example that I hope will guide you in the right direction.
To start here's an example of a button template that you could define in the resource dictionary of your choosing. Note the visibility binding on the image and the text binding on the textblock, these properties will be located on a Pushpin_ViewModel we'll define later:
<Style x:Name="PushpinButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused"/>
<VisualState x:Name="Unfocused"/>
</VisualStateGroup>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed"/>
<VisualState x:Name="Disabled"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Height="50" Width="50" Source="Pushpins/pushpinSeaplane.png" Visibility="{Binding PushpinImageVisibility}" />
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="39" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Black">
<TextBlock Grid.Row="0" Foreground="White" Text="{Binding PushpinLabelText}" TextWrapping="Wrap" Margin="5" />
</Grid>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In your primary view where your pushpins are displayed you may have some sort of items control responsible for displaying your pushpins. Here is an example of such an items control where the data template is a button with two important features 1) the button style we defined above and 2) a click event that will call a toggle method on the corresponding pushpin view model:
<ItemsControl ItemsSource="{Binding Pushpins}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Name="PushpinButton" Click="pushpinButton_Click" DataContext="{Binding}" Style="{StaticResource PushpinButtonStyle}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
private void pushpinButton_Click(object sender, RoutedEventArgs e)
{
Pushpin_ViewModel pushpin_ViewModel = ((Button)sender).DataContext as Pushpin_ViewModel;
pushpin_ViewModel.TogglePushpinVisibility();
}
The following view model class would represent the data context of your primary view (the one that contains the items control we defined above). Here we have the collection of pushpins that populate the items control:
public class PrimaryPushpinView_ViewModel : INotifyPropertyChanged
{
public PushpinView_ViewModel()
{
this.Pushpins.Add(new Pushpin_ViewModel() { PushpinLabelText="First Pushpin" });
}
public List<Pushpin_ViewModel> Pushpins
{
get { return pushpins; }
set
{
if (value != pushpins)
{
pushpins = value;
OnPropertyChanged("Pushpins");
}
}
}
private List<Pushpin_ViewModel> pushpins = new List<Pushpin_ViewModel>();
}
And finally here is a representation of the pushpin view model. There would be one instance of this class per pushpin in your pushpin collection:
public class Pushpin_ViewModel : INotifyPropertyChanged
{
public Visibility PushpinVisibility
{
get { return pushpinVisibility; }
set
{
if (value != pushpinVisibility)
{
pushpinVisibility= value;
OnPropertyChanged("PushpinVisibility");
}
}
}
private Visibility pushpinVisibility;
public String PushpinLabelText
{
get { return pushpinLabelText; }
set
{
if (value != pushpinLabelText)
{
pushpinLabelText= value;
OnPropertyChanged("PushpinLabelText");
}
}
}
private String pushpinLabelText;
public void TogglePushpinVisibility()
{
this.PushpinVisibility = this.PushpinVisibility.Equals(Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible;
}
}
Sorry it took a while to get back to you, crazy day today, hope this helps out.

How do I access elements that have been dynamically assigned to a control in the form of an XAML resource?

I have the following resource in my window that declares how a certain kind of TabItem should look like.
<Window.Resources>
<StackPanel x:Key="TabSearchContents" x:Shared="False"
Orientation="Vertical">
<Border
BorderThickness="3"
BorderBrush="Purple">
<TextBlock
Text="SEARCH BOOKS"
FontFamily="Verdana"
FontSize="25"
Foreground="Blue"
HorizontalAlignment="Center" />
</Border>
<StackPanel
Height="30"
Orientation="Horizontal"
Margin="5">
<TextBox
x:Name="txtSearch"
Width="650"
FontFamily="Comic Sans MS"
Foreground="Chocolate" />
<Button
x:Name="btnSearch"
Width="100"
Content="Go!"
Click="BtnSearch_Click" />
</StackPanel>
<Grid x:Name="gridResults">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="450"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" VerticalScrollBarVisibility="Auto">
<ItemsControl x:Name="itmsSearch" ItemsSource="{Binding}" Padding="4"
ItemTemplate="{StaticResource SearchResultItemDT}">
</ItemsControl>
</ScrollViewer>
<StackPanel x:Name="stkpnlDetails">
</StackPanel>
</Grid>
</StackPanel>
</Window.Resources>
Then, in my code-behind, I dynamically create a tab and assign to the TabControl that is already present in my window.
void BtnNewTab_Click(object sender, RoutedEventArgs e)
{
TabItem tb = new TabItem();
tb.Content = this.Resources["TabSearchContents"];
tb.DataContext = _bridge.SearchBooksByTitle("e");
tb.Header = "Wuttp yo!";
Button btnGo = ((Button)tb.FindName("btnSearch"));
ItemsControl i = (ItemsControl)tb.FindName("itmsSearch");
btnGo.Resources.Add("ResultList", i);
daTabs.Items.Add(tb);
tb.Focus();
}
I want to access the btnSearch Button that is declared in my XAML resource.
As it is, this code throws an exception since btnGo turns out to be null (as well as i) since it can't find the expected control via FindName().
I read about the RegisterName() method, but it requires a reference to an instance of the required control... which I don't have.
I dont think you should define your button like this, try defining it in a style, creating a button and assigning the button that style, i think you will be able to get what you are going for this way.
myTheme.xaml
<ResourceDictionary
<Style x:Key="btnSearch" TargetType="{x:Type Button}">
<Setter Property="Width" Value="100"/>
<Setter Property="Content" Value="Go!"/>
<Setter Property="Click" Value="btn_Click"/>
</Style>
ResourceDictionary/>
myCode.cs
Button btnGo = new Button;
btnGo.Style = "{DynamicResource btnSearch}";
Hope this helps,
Eamonn

How to add scrolling buttons to a data bound, horiztonal list

So I have a List, which contains items. Right now they are thumbnails of pictures. I wanted this list to be bound to a changing list in code behind, so I used a Listbox. However, I needed this box to flow horizontally. So it is styled as a StackPanel. Lastly, I want buttons to control the scrolling, not scrollbars. That's the part that does not work Here's a code sample :
<UserControl x:Class="TestBench.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<Style x:Key="StackHorz" TargetType="ListBox">
<Style.Setters>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Background="AliceBlue" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ScrollViewer BorderBrush="DarkGreen" BorderThickness="2" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Button x:Name="_Next" Content="NEXT" Height="20" Width="40" VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
<Button x:Name="_Prev" Content="PREV" Height="20" Width="40" VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
<ListBox x:Name="TestList" Height="100" Width="800" VerticalAlignment="Top">
...Insert ListItems...
</ListBox>
</Grid>
In this example the listbox is not bound, but I do need to be able to set ItemsSource={Binding Content}. Code behind I tried was :
namespace TestBench
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
TestList.Style = this.Resources["StackHorzTop"] as Style;
_Next.Click += new RoutedEventHandler(_Next_Click);
_Prev.Click += new RoutedEventHandler(_Prev_Click);
}
void _Prev_Click(object sender, RoutedEventArgs e)
{
TestList.ScrollIntoView(TestList.Items[0]);
}
void _Next_Click(object sender, RoutedEventArgs e)
{
TestList.ScrollIntoView(TestList.Items[TestList.Items.Count - 1]);
}
}
}
But the ScrollIntoView does nothing. I also tried getting the ScrollViewer as a VisualTreeHelper.GetChild() of the list box, but scrolling there using ScrollToHorizontalOffset() does nothing either.
I know it's a weird way to set things up, but I need all 3 functionalities (Binding, Horizontal orientation, No scroll bars with button scrolling). Anyone know where I am going wrong on this one?
Thanks in advance,
Chart.
Maybe you can try putting your listbox in a ScrollViewer and style the ScrollViewer so that the scroll bars are not visible (only the buttons).
More info on the ScrollViewer's templatable parts can be found here.

WPF Toolbar Items HorizontalAligment="Right"

Is it possible to make the elements within a WPF toolbar have a HorizontalAlignment of Right?
<ToolBar Height="38" VerticalAlignment="Top" Grid.Row="1">
<Button HorizontalAlignment="Left" Width="50" VerticalAlignment="Stretch"/>
<Button HorizontalAlignment="Left" Width="50" VerticalAlignment="Stretch"/>
<ComboBox Width="120" HorizontalAlignment="Right"/>
</ToolBar>
I've tried adding the elements inside into a Grid and assigning the ColumnDefinitions to Left/Right as well. I have also tried a StackPanel. No matter what I try I can't seem to get the ComboBox to be "anchored" on the right side of the Toolbar.
UPDATE:
<DockPanel LastChildFill="True">
Doesn't work, It will not fill the ToolBar element like it would a normal element.
Further investigation showed that in order to do this I need to set the width of a Grid within the ToolBar, or as Chris Nicol said, a DockPanel within the ToolBar dynamically to that of the width of the Toolbar using RelativeSource.
However, that did not feel like a clean solution. It is quite complicated to get the Toolbar to update correctly on resizing. So instead I found somewhat of a hack that looks, and operates cleaner by adding an external Grid.
<Grid>
<ToolBar Height="38" VerticalAlignment="Top" Grid.Row="1">
<Button HorizontalAlignment="Left" Width="50" VerticalAlignment="Stretch"/>
<Button HorizontalAlignment="Left" Width="50" VerticalAlignment="Stretch"/>
</ToolBar>
<ComboBox Margin="0,0,15,0" Width="120" HorizontalAlignment="Right" Grid.Row="1"/>
</Grid>
Since all of my elements are on a Grid, I can place my ComboBox on top of the ToolBar by assigning it's Grid.Row to the same row as the toolbar. After setting my Margins to pull the ComboBox over slightly as not to interfere with looks, it operates as needed with no bugs. Since the only other way I found to do this was setting a DockPanel/Grid's Width property dynamically, I actually feel like this is the cleaner more efficient way to do it.
I realize this is an old topic, but it still pops up when asking the question. This is how I handle this question:
<Grid>
<Grid.RowDefinitions>
<RowDefinition x:Name="MenuRow" Height="25"/>
<RowDefinition x:Name="ToolbarRow" Height="25"/>
<RowDefinition x:Name="CatalogRow" Height="*"/>
<RowDefinition x:Name="RecipeRow" Height="0.4*"/>
</Grid.RowDefinitions>
<ToolBar Grid.Row="1">
<Button x:Name="tbFileOpen" Margin="0,0,0,0" Click="MenuItem_Click"><Image Source="Icons/Main/File/Load.png"/></Button>
<Button x:Name="tbFileSave" Margin="0,0,0,0" Click="MenuItem_Click"><Image Source="Icons/Main/File/Save.png"/></Button>
<Button x:Name="tbFileClear" Margin="0,0,0,0" Click="MenuItem_Click"><Image Source="Icons/Main/File/New document.png"/></Button>
</ToolBar>
<ToolBar Grid.Row="1" HorizontalAlignment="Right">
<Button x:Name="tbFileExit" Margin="0,0,0,0" Click="MenuItem_Click"><Image Source="Icons/Main/File/Exit.png"/></Button>
</ToolBar>
</Grid>
Effectively: I create two toolbar objects and have them on the same Grid.row. The first one has default (left) alignment, the second one is right aligned. It seems to do the trick for me.
For anyone else looking for a solution, the following worked for me:
<ToolBar HorizontalAlignment="Stretch" ToolBarTray.IsLocked="True">
<ToolBar.Resources>
<Style TargetType="{x:Type DockPanel}">
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
</ToolBar.Resources>
I'm using .NET 4.6 and VS2015, but I believe this would work in previous versions too.
Have you tried using a DockPanel that fills the toolbar, then you can dock the ComboBox to the right.
Remember that with a dockpanel the sequence you put the items in is very important.
HTH
<ToolBar Width="100" VerticalAlignment="Top" >
<ToolBar.Resources>
<Style TargetType="{x:Type ToolBarPanel}">
<Setter Property="Orientation" Value="Vertical"/>
</Style>
</ToolBar.Resources>
<DockPanel>
<ToolBarPanel Orientation="Horizontal" >
<Button>A</Button>
<Button>B</Button>
</ToolBarPanel>
<Button DockPanel.Dock="Right" HorizontalAlignment="Right">C</Button>
</DockPanel>
</ToolBar>
My solution to this was to create a label control with a "spring" like ability, so that it would fill the empty void with between the buttons on the toolbar, thus "right aligning" the toolbar's combobox (or any other control that needs "right-aligned).
To do this, I created a WidthConverter, that would take the Actual Width of the ToolBar Control, and then subtract the the space needed needed to right align the combobox.:
public class WidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Math.Max(System.Convert.ToDouble(value) - System.Convert.ToDouble(parameter), 0.0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then, I added a label control to the toolbar, placed to the left of the combobox you need right aligned. Bind the label's Width to the toolbar's ActualWidth and apply the WidthConverter:
<Label Width="{Binding Converter={StaticResource WidthConverter}, ElementName=toolBar1, Path=ActualWidth, ConverterParameter=50}" />
You will need to adjust the ConverterParameter to your specific needs until you get the desired "right align". A higher number provides more space for the combobox, whereas a lower number provides less space.
Using this solution, the label will automatically resize whenever your toolbar resizes, making it appear that you have right aligned your combobox.
There are two great benefit to this solution compared to adding a grid to the toolbar. The first is that if you need to use buttons on the toolbar, you won't lose the toolbar button styling. The second is that the overflow will work as expected if the toolbar length is reduced through window resizing. Individual buttons will go into the overflow as required. If the buttons are put into a a grid then the grid is put into the overflow taking all buttons with it.
XAML of it in use:
<ToolBarPanel>
<ToolBar Name="toolBar1">
<Button>
<Image Source="save.png"/>
</Button>
<Label Width="{Binding Converter={StaticResource Converters.WidthConverter},
ElementName=toolBar1,
Path=ActualWidth,
ConverterParameter=231}" HorizontalAlignment="Stretch" ToolBar.OverflowMode="Never"/>
<Button>
<Image Source="open.png"/>
</Button>
</ToolBar>
If you desire to always keep the last button on the toolbar, say a help button that you always want visible, add the attribute ToolBar.OverflowMode="Never" to its element.
This is how I did it:
I created a style for the toolbar
<Style x:Key="{x:Type ToolBar}" TargetType="{x:Type ToolBar}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToolBar}">
<Grid Background="{StaticResource ToolGridBackground}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Style="{StaticResource LogoImage}"/>
<ToolBarPanel Grid.Column="2" x:Name="PART_ToolBarPanel" IsItemsHost="true" Margin="0,1,2,2" Orientation="Horizontal"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The important part is :
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
And
<ToolBarPanel Grid.Column="2"/>
With this, your buttons will be right aligned
I'm not very satisfied with the "WidthConverter" solution because I got some dynamic elements at end. Further search led me to here, which seems to be working perfect for me. Here is my code sample in case you are interested:
<ToolBar Name="toolBar">
<DockPanel Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type ToolBarPanel}}}">
<DockPanel.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"></Style>
</DockPanel.Resources>
<Button x:Name="btnRefresh" ToolTip="Refresh" Click="btnRefresh_Click">
<Image Margin="2 0" Source="/Resources/refresh.ico" Height="16" Width="16"/>
</Button>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Image Margin="2 0" Source="/Resources/Help.ico" Height="16" Width="16"/>
<TextBlock Text="Help" Margin="2 0" VerticalAlignment="Center"/>
</StackPanel>
</DockPanel>
</ToolBar>

How do I space out the child elements of a StackPanel?

Given a StackPanel:
<StackPanel>
<TextBox Height="30">Apple</TextBox>
<TextBox Height="80">Banana</TextBox>
<TextBox Height="120">Cherry</TextBox>
</StackPanel>
What's the best way to space out the child elements so that there are equally-sized gaps between them, even though the child elements themselves are of different sizes? Can it be done without setting properties on each of the individual children?
Use Margin or Padding, applied to the scope within the container:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="0,10,0,0"/>
</Style>
</StackPanel.Resources>
<TextBox Text="Apple"/>
<TextBox Text="Banana"/>
<TextBox Text="Cherry"/>
</StackPanel>
EDIT: In case you would want to re-use the margin between two containers, you can convert the margin value to a resource in an outer scope, f.e.
<Window.Resources>
<Thickness x:Key="tbMargin">0,10,0,0</Thickness>
</Window.Resources>
and then refer to this value in the inner scope
<StackPanel.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="{StaticResource tbMargin}"/>
</Style>
</StackPanel.Resources>
Another nice approach can be seen here:
http://blogs.microsoft.co.il/blogs/eladkatz/archive/2011/05/29/what-is-the-easiest-way-to-set-spacing-between-items-in-stackpanel.aspx
Link is broken -> this is webarchive of this link.
It shows how to create an attached behavior, so that syntax like this would work:
<StackPanel local:MarginSetter.Margin="5">
<TextBox Text="hello" />
<Button Content="hello" />
<Button Content="hello" />
</StackPanel>
This is the easiest & fastest way to set Margin to several children of a panel, even if they are not of the same type. (I.e. Buttons, TextBoxes, ComboBoxes, etc.)
I improved on Elad Katz' answer.
Add LastItemMargin property to MarginSetter to specially handle the last item
Add Spacing attached property with Vertical and Horizontal properties that adds spacing between items in vertical and horizontal lists and eliminates any trailing margin at the end of the list
Source code in gist.
Example:
<StackPanel Orientation="Horizontal" foo:Spacing.Horizontal="5">
<Button>Button 1</Button>
<Button>Button 2</Button>
</StackPanel>
<StackPanel Orientation="Vertical" foo:Spacing.Vertical="5">
<Button>Button 1</Button>
<Button>Button 2</Button>
</StackPanel>
<!-- Same as vertical example above -->
<StackPanel Orientation="Vertical" foo:MarginSetter.Margin="0 0 0 5" foo:MarginSetter.LastItemMargin="0">
<Button>Button 1</Button>
<Button>Button 2</Button>
</StackPanel>
The thing you really want to do is wrap all child elements. In this case you should use an items control and not resort to horrible attached properties which you will end up having a million of for every property you wish to style.
<ItemsControl>
<!-- target the wrapper parent of the child with a style -->
<ItemsControl.ItemContainerStyle>
<Style TargetType="Control">
<Setter Property="Margin" Value="0 0 5 0"></Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<!-- use a stack panel as the main container -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- put in your children -->
<ItemsControl.Items>
<Label>Auto Zoom Reset?</Label>
<CheckBox x:Name="AutoResetZoom"/>
<Button x:Name="ProceedButton" Click="ProceedButton_OnClick">Next</Button>
<ComboBox SelectedItem="{Binding LogLevel }" ItemsSource="{Binding LogLevels}" />
</ItemsControl.Items>
</ItemsControl>
+1 for Sergey's answer. And if you want to apply that to all your StackPanels you can do this:
<Style TargetType="{x:Type StackPanel}">
<Style.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="{StaticResource tbMargin}"/>
</Style>
</Style.Resources>
</Style>
But beware: if you define a style like this in your App.xaml (or another dictionary that is merged into the Application.Resources) it can override the default style of the control. For mostly lookless controls like the stackpanel it isn't a problem, but for textboxes etc you may stumble upon this problem, which luckily has some workarounds.
Following up on Sergey's suggestion, you can define and reuse a whole Style (with various property setters, including Margin) instead of just a Thickness object:
<Style x:Key="MyStyle" TargetType="SomeItemType">
<Setter Property="Margin" Value="0,5,0,5" />
...
</Style>
...
<StackPanel>
<StackPanel.Resources>
<Style TargetType="SomeItemType" BasedOn="{StaticResource MyStyle}" />
</StackPanel.Resources>
...
</StackPanel>
Note that the trick here is the use of Style Inheritance for the implicit style, inheriting from the style in some outer (probably merged from external XAML file) resource dictionary.
Sidenote:
At first, I naively tried to use the implicit style to set the Style property of the control to that outer Style resource (say defined with the key "MyStyle"):
<StackPanel>
<StackPanel.Resources>
<Style TargetType="SomeItemType">
<Setter Property="Style" Value={StaticResource MyStyle}" />
</Style>
</StackPanel.Resources>
</StackPanel>
which caused Visual Studio 2010 to shut down immediately with CATASTROPHIC FAILURE error (HRESULT: 0x8000FFFF (E_UNEXPECTED)), as described at https://connect.microsoft.com/VisualStudio/feedback/details/753211/xaml-editor-window-fails-with-catastrophic-failure-when-a-style-tries-to-set-style-property#
Grid.ColumnSpacing, Grid.RowSpacing, StackPanel.Spacing are now on UWP preview, all will allow to better acomplish what is requested here.
These properties are currently only available with the Windows 10 Fall Creators Update Insider SDK, but should make it to the final bits!
The UniformGrid might not be available in Silverlight, but someone has ported it from WPF. http://www.jeff.wilcox.name/2009/01/uniform-grid/
My approach inherits StackPanel.
Usage:
<Controls:ItemSpacer Grid.Row="2" Orientation="Horizontal" Height="30" CellPadding="15,0">
<Label>Test 1</Label>
<Label>Test 2</Label>
<Label>Test 3</Label>
</Controls:ItemSpacer>
All that's needed is the following short class:
using System.Windows;
using System.Windows.Controls;
using System;
namespace Controls
{
public class ItemSpacer : StackPanel
{
public static DependencyProperty CellPaddingProperty = DependencyProperty.Register("CellPadding", typeof(Thickness), typeof(ItemSpacer), new FrameworkPropertyMetadata(default(Thickness), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnCellPaddingChanged));
public Thickness CellPadding
{
get
{
return (Thickness)GetValue(CellPaddingProperty);
}
set
{
SetValue(CellPaddingProperty, value);
}
}
private static void OnCellPaddingChanged(DependencyObject Object, DependencyPropertyChangedEventArgs e)
{
((ItemSpacer)Object).SetPadding();
}
private void SetPadding()
{
foreach (UIElement Element in Children)
{
(Element as FrameworkElement).Margin = this.CellPadding;
}
}
public ItemSpacer()
{
this.LayoutUpdated += PART_Host_LayoutUpdated;
}
private void PART_Host_LayoutUpdated(object sender, System.EventArgs e)
{
this.SetPadding();
}
}
}
Usually, I use Grid instead of StackPanel like this:
horizontal case
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBox Height="30" Grid.Column="0">Apple</TextBox>
<TextBox Height="80" Grid.Column="2">Banana</TextBox>
<TextBox Height="120" Grid.Column="4">Cherry</TextBox>
</Grid>
vertical case
<Grid>
<Grid.ColumnDefinitions>
<RowDefinition Width="auto"/>
<RowDefinition Width="*"/>
<RowDefinition Width="auto"/>
<RowDefinition Width="*"/>
<RowDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<TextBox Height="30" Grid.Row="0">Apple</TextBox>
<TextBox Height="80" Grid.Row="2">Banana</TextBox>
<TextBox Height="120" Grid.Row="4">Cherry</TextBox>
</Grid>
sometimes you need to set Padding, not Margin to make space between items smaller than default

Resources