WPF: How can I stretch the middle child in a DockPanel? - c#

I added a DockPanel to a RadioButton element such that I can distribute the radio button label, a textbox and a button horizontally using 100% of the width.
Using LastChildFill="True"within the DockPanel stretches the last element. This works out nicely if the textbox is the last child in the panel. But, as the button is the last element and has a fixed width, the textbox should be stretched. However, there's no such property like 2ndChildFill="True".
My code looks like this:
<RadioButton HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<DockPanel >
<TextBlock VerticalAlignment="Center">in location:</TextBlock>
<TextBox Grid.Column="1" Margin="10,0,0,0">Path string</TextBox>
<Button HorizontalAlignment="Right"
Margin="10,0,0,0" Padding="3,0">...</Button>
</DockPanel>
</RadioButton>
And it gives me this:
Any ideas, hints to fix this? Many thanks in advance...

You need to set DockPanel.Dock attached property for your elements and leave TextBox as the last element:
<RadioButton HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Left"
VerticalAlignment="Center"
Text="in location:" />
<Button DockPanel.Dock="Right"
Margin="10,0,0,0"
Padding="3,0"
Content="..." />
<TextBox Margin="10,0,0,0">
Path string
</TextBox>
</DockPanel>
</RadioButton>

Accepted answer is fixing your problem but creates another one. If someone uses keyboard (TAB) to navigate through your interface, Button will be focused before TextBox. This could be very annoying on a long run. If you don't want to brake your tab orders use Grid instead of DockPanel:
<RadioButton HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" VerticalAlignment="Center">in location:</TextBlock>
<TextBox Grid.Column="1" Margin="10,0,0,0">Path string</TextBox>
<Button Grid.Column="2" HorizontalAlignment="Right" Margin="10,0,0,0" Padding="3,0">...</Button>
</Grid>
</RadioButton>
Alternative would be to control tab orders yourself using TabIndex attribute. This can be tricky though, especially when you display your control in a collection.
<RadioButton HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" TabIndex="0">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Text="in location:" />
<Button DockPanel.Dock="Right" Margin="10,0,0,0" Padding="3,0" Content="..." TabIndex="2"/>
<TextBox Margin="10,0,0,0" TabIndex="1">Path string</TextBox>
</DockPanel>
</RadioButton>

Related

How to add a separator to the Expander component in UWP app?

I'm looking for a way of implementing this kind of separator to the expander component in UWP app:
Any ideas how to do that?
I've managed to create the similar look of the expander, but I don't know how to implement separators?
Also, I would like to learn how to make expanders kind of "push" other components down when I expand them, so they don't overlap with other components, but simply move them.
Here's the XAML code for this expander:
<muxc:Expander x:Name="FontExpander"
x:Uid="FontExpander"
Width="1155"
IsExpanded="True"
Margin="22,323,0,0"
VerticalAlignment="Top"
Grid.Row="2"
Height="380"
HorizontalContentAlignment="Left"
ExpandDirection="Down">
<muxc:Expander.Header>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<FontIcon Margin="0,0,12,0"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
FontSize="16"
Glyph=""/>
<StackPanel Grid.Column="1"
Margin="0,12"
Orientation="Vertical">
<TextBlock x:Uid="SettingsFontTitle" Style="{StaticResource BodyTextBlockStyle}"/>
<TextBlock x:Uid="SettingsFontDescription"
Style="{StaticResource CaptionTextBlockStyle}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
TextWrapping="WrapWholeWords"/>
</StackPanel>
</Grid>
</muxc:Expander.Header>
<muxc:Expander.Content>
<Grid Margin="-18,-170,-17,-170">
<TextBlock x:Name="SettingsFamily" x:Uid="SettingsFamily" Text="Family" Margin="50,51,1000,286"/>
<ComboBox x:Name="ComboBoxFamily" ItemsSource="{x:Bind Fonts}" Width="200" Margin="950,41,0,0" SelectionChanged="ComboBoxFamily_SelectionChanged" />
<TextBlock x:Name="SettingsStyle" x:Uid="SettingsStyle" Text="Style" Margin="50,106,995,218" Loaded="SettingsStyle_Loaded"/>
<ComboBox x:Name="ComboBoxStyle" Width="200" Margin="950,101,0,0">
<x:String>Regular</x:String>
</ComboBox>
<TextBlock x:Name="SettingsSize" x:Uid="SettingsSize" Text="Size" Margin="50,166,1002,158"/>
<ComboBox x:Name="ComboBoxSize" ItemsSource="{x:Bind FontSizes}" Width="200" Margin="950,161,0,0"/>
<TextBlock x:Name="Text" x:Uid="SettingsText" Text="Hello World! I am so excited to be here!" Margin="62,224,38,126" TextWrapping="Wrap" FontSize="{x:Bind (x:Double)ComboBoxSize.SelectedValue, Mode=OneWay}" TextAlignment="Center"/>
</Grid>
</muxc:Expander.Content>
</muxc:Expander>
Any help would be much appreciated!
A separate can for example be implemented using a Border or a Grid:
<Grid Background="DarkGray" Height="1" />
As a side note, you shouldn't use margins to position your elements. Use an appropriate layout Panel. Using a Grid, you should add ColumnDefinitions and RowDefinitions to it as examplified here.

Force Size on a Viewbox's Children

So I have a rather interesting question. I have a viewbox that has a few elements in it (a custom user control for an image, a canvas, a label, and a textbox). What I want is to try and have all elements scale with the viewbox, but I want the label and the textbox to have a "Max Size." I have tried using a max width and height on these controls but they seem to ignore it. If someone could take a look at my code below an slap me for what I am doing wrong that would be appreciated.
<Viewbox Name="myViewBox" Stretch="Uniform">
<!--Grid used to track mouse movements in this element for other reasons -->
<Grid Name="grdViewboxGrid" MouseMove="trackMouse">
<Canvas Name="cvsViewboxCanvas" MinWidth="270" MinHeight="270"
VerticalAlignment="Top" HorizontalAlignment="Center"
Panel.ZIndex="1" Background="Black"
MouseUp="Canvas_MouseUp"
MouseMove="Canvas_MouseMove">
<Grid>
<!--Would rather not post here for Intellectual Property reasons-->
<!-- Extension of the image control -->
<CustomImageUserControl />
<Grid>
<Grid Width="{Binding LabelWidthPercentage}"
MaxWidth="50"
Height="{Binding LabelHeightPercentage"
MaxHeight="26"
SnapsToDevicePixels="True" VerticalAlignment="Top"
HorizontalAlignment="Left" Margin="5" IsHitTestVisible="False">
<Label Name="lblViewboxLabel" HorizontalAlignment="Left"
Padding="5,5,5,0" Margin="0,5,0,0"
Style="{x:Null}"
Content="{Binding lblContent}" />
</Grid>
<Grid>
<Grid Width="{Binding TextBoxWidthPercentage}"
MaxWidth="156"
Height="{Binding TextBoxHeightPercentage}"
MaxHeight="45"
SnapsToDevicePixels="True" Vertical="Top"
HorizontalAlignment="Right" Margin="5" IsHitTestVisible="False">
<Border Style="{DynamicResource CustomBorder}" />
<Grid>
<Textbox Name="txtViewboxTextBox" Text="{Binding txtViewbox}" />
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
</Canvas>
</Grid>
</Viewbox>
If I am not including something that is needed please let me know and I will update my question. Any help would be greatly appreciated this is now day 4 on this issue sadly :-(
I am not sure why you need so many overlapping Grids, but I hope that I can answer your question nevertheless:
In order to have the label left of the text box and to assign a maximum width to each of these two controls, use a Grid with two columns and set the MaxWidth property for each column. Then, assign the label to the left column (the one with index 0) and assign the text box to the right column (index 1). The corresponding code fragment looks like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="30"/>
<ColumnDefinition MaxWidth="156" MinWidth="30"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" x:Name="lblViewboxLabel" HorizontalAlignment="Left" Foreground="Yellow"
Padding="5,5,5,0" Margin="0,5,0,0"
Style="{x:Null}"
Content="{Binding lblContent}" />
<TextBox Grid.Column="1" x:Name="txtViewboxTextBox" Text="{Binding txtViewbox}" Background="Orange"/>
</Grid>
I also have assigned a MinWidth to the right column; this is necessary to make sure that the text box does not completely disappear if it contains no text.

How to change GridView.ItemTemplate while app is running?

In my app I have page with GridView and ComboBox. I want to change GridView.ItemTemplate property according to selected item in ComboBox. How should I implement it?
btw, I know about this question, but it is quite old and it does not look like "best practice". (How visibility/invisibility of ui control affects cpu/gpu load?)
My GridView:
<GridView x:Name="gridViewMain" Grid.Row="1" SelectionMode="None" IsItemClickEnabled="True"
ItemsSource="{Binding CurrentList}" ItemTemplate="{StaticResource gridViewMainItemTemplate}"
Loaded="gridViewMain_Loaded" LayoutUpdated="gridViewMain_LayoutUpdated">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ItemClick">
<core:CallMethodAction MethodName="GridViewClick"
TargetObject="{Binding Mode=OneWay}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</GridView>
One of my templates:
<DataTemplate x:Key="gridViewMainItemTemplate">
<Grid x:Name="gridATemplate" Width="185" Height="288">
<Image x:Name="imgATemplate" Source="{Binding image_url}" Stretch="UniformToFill"
HorizontalAlignment="Center" VerticalAlignment="Center" />
<Grid Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}" VerticalAlignment="Bottom">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock x:Name="textBlockTitle" Text="{Binding title}"
TextWrapping="Wrap" Style="{StaticResource BodyTextBlockStyle}" Margin="5,0,0,0"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Foreground="White" FontWeight="Bold"
MaxHeight="50" />
<TextBlock x:Name="textBlockType" TextWrapping="Wrap" Style="{StaticResource BodyTextBlockStyle}"
Margin="5,0,0,0"
Grid.Column="0" Grid.Row="1" Foreground="White" Text="{Binding type}" FontWeight="Bold" />
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal">
<TextBlock x:Name="textBlockProgressL" TextWrapping="Wrap"
Style="{StaticResource BodyTextBlockStyle}" FontWeight="Bold" Foreground="White"
Text="Progress:" />
<TextBlock x:Name="textBlockProgressV" TextWrapping="Wrap"
Style="{StaticResource BodyTextBlockStyle}" FontWeight="Bold" Foreground="White"
Text="{Binding watched_episodes}" Margin="10,0,0,10" />
</StackPanel>
</Grid>
</Grid>
</DataTemplate>
Sure you can do this! In XAML you can do anything. What you cannot do is change the Template on the fly without re-rendering. Remember, this is like telling your printer to use card stock. It will obey. If you change the setting to use notebook paper, it will obey that, too. You will just have to print again since it has already printed on card stock.
There are a few ways for you to re-render a GridView. One way is to navigate away from the page and navigate back. That's not ideal sounding in your scenario. Odds are, in your scenario, you just need to reset the ObservableCollection you are using. Like this:
void Reset<T>(ObservableCollection<T> collection)
{
var original = collection.ToArray();
collection.Clear();
foreach (var item in original)
collection.Add(item);
}
Best of luck!
You'll want to use datatemplateselector
http://blogs.msdn.com/b/bryanbolling/archive/2012/12/08/how-to-control-the-datatemplateselector-in-windows-store-apps.aspx
You can create multiple itemtemplates and choose which one to display based on any condition.
You'll have to refresh the gridview whenever the selection changes.

Automatically size TextBlock length to length of characters (in a Grid)

I have a Grid that I populate with a TextBlock and another Grid, which is initially hidden. Since I have the TextBlock to respond to click events, I only want the method to be called when the user clicks on the text itself, not anywhere in the Grid. This is what my code looks like right now:
<Grid x:Name="LayoutRoot" Height="25">
<TextBlock x:Name="NewLabelButton" Text="Add New" Width="Auto" Foreground="Blue" TextDecorations="Underline" MouseLeftButtonUp="AddNew_OnClick" VerticalAlignment="Center"/>
<Grid x:Name="NewLabelPanel" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="NameBox" TextChanged="NameBox_OnTextChanged" KeyDown="NameBox_OnKeyDown" Grid.Column="0"/>
<Button x:Name="OKButton" Content="OK" Click="OKButton_OnClick" IsEnabled="False" Margin="2,0,0,0" Grid.Column="1"/>
<Button x:Name="CancelButton" Content="Cancel" Click="CancelButton_OnClick" Margin="2,0,0,0" Grid.Column="2"/>
</Grid>
</Grid>
Which results in the TextBlock spanning the whole width of the Grid, making it clickable from essentially anywhere in the Grid. What I want is for it to only be as wide as it needs to be to display the text (which could vary, so I can't use a static width).
Using a StackPanel instead of a Grid resolves the TextBlock width issue, but then the child Grid doesn't expand to fill the entire StackPanel, which it needs to.
Encapsulating the TextBlock in a StackPanel works, but I'm wondering if there is a better (and more efficient) way of doing it.
<Grid x:Name="LayoutRoot" Height="25">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="NewLabelButton" Text="Add New" Foreground="Blue" TextDecorations="Underline" MouseLeftButtonUp="AddNew_OnClick" VerticalAlignment="Center"/>
</StackPanel>
<Grid x:Name="NewLabelPanel" Visibility="Collapsed">
So my question is this: Is there a better way to do this than with my little StackPanel hack, or is that the only way? Thanks!
Try setting the HorizontalAlignment of the TextBlock to the left of the grid.
<TextBlock x:Name="NewLabelButton" HorizontalAlignment="Left" Text="Add New" Width="Auto" Foreground="Blue" TextDecorations="Underline" MouseLeftButtonUp="AddNew_OnClick" VerticalAlignment="Center"/>

Can't seem to set Combobox.Background

I'm developing a Turing Machine simulator for a class on theory, and I'm trying to change the background color of the input area based upon whether the machine would accept the language (basically, one color over the other depending on if it's valid input).
Since I want to provide a couple example inputs, it needs to be a ComboBox. Since the professor needs to test his own inputs, it must be editable as well. So, here we are.
I've tried setting the ComboBox.Background property both programmatically and using XAML (via the Property editor), and neither work. I have no problem setting ComboBox.Foreground, however.
Here is my XAML:
<Window x:Class="Turing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Turing Machine Emulator" Height="400" Width="600" Loaded="onload" MinHeight="500" MinWidth="600">
<Grid>
<ComboBox x:Name="drpProblem" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="120" SelectionChanged="changeproblem"/>
<Label x:Name="lblDescription" Content="Language Description" Margin="135,7,90,0" VerticalAlignment="Top"/>
<Grid Margin="10,0,10,35" Height="24" VerticalAlignment="Bottom">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="10*"/>
</Grid.ColumnDefinitions>
<Label x:Name="lblLeft" Content="left" HorizontalContentAlignment="Right" VerticalContentAlignment="Center" Padding="0" Margin="0,0,10,0" FontFamily="Consolas"/>
<Label x:Name="lblRight" Content="right" Grid.Column="2" VerticalContentAlignment="Center" Padding="0" Margin="10,0,0,0" FontFamily="Consolas"/>
<Label x:Name="lblCenter" Content="cur" Grid.Column="1" Height="24" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Background="#FFC5FFA4" FontFamily="Consolas" FontSize="16"/>
</Grid>
<Button x:Name="btnIterate" Content="Iterate" Margin="10,0,0,64" Height="20" VerticalAlignment="Bottom" Click="btnIterate_Click" HorizontalAlignment="Left" Width="236"/>
<!-- This one right here -->
<ComboBox x:Name="txtInput" Height="23" Margin="10,0,10,89" Text="Input String" VerticalAlignment="Bottom" FontFamily="Consolas" VerticalContentAlignment="Center" TextBoxBase.TextChanged="cboGetInput" BorderBrush="{x:Null}" Foreground="Black" Background="#FF874343" IsEditable="True" />
<TextBox x:Name="txtMs" Height="20" Margin="251,0,172,64" TextWrapping="Wrap" Text="wait (seconds)" VerticalAlignment="Bottom"/>
<Button x:Name="btnAutoRun" Content="AutoRun" Margin="0,0,10,64" Click="btnAutoRun_Click" Height="20" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="157"/>
<TextBox x:Name="txtTM" Margin="10,38,10,142" TextWrapping="Wrap" Text="Language" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" FontFamily="Consolas" FontSize="14"/>
<Button x:Name="btnLoadLang" Content="Load" Margin="10,0,10,117" Height="20" VerticalAlignment="Bottom" Click="changeproblem"/>
<StatusBar Height="30" VerticalAlignment="Bottom">
<TextBlock x:Name="stTXTName" Text="StateName"/>
<Separator/>
<TextBlock x:Name="stTXTDescription" Text="StateDescription"/>
<Separator/>
<TextBlock x:Name="stTXTTransition" Text="NextTransition"/>
<Separator/>
<TextBlock x:Name="stTXTNext" Text="NextState"/>
</StatusBar>
</Grid>
</Window>
and here is the code I'm using to try to change the colors around:
if (TM.AcceptsString(txtInput.Text))
{
txtInput.Background = Brushes.LightGreen;
txtInput.Foreground = Brushes.LightGreen;
}
else
{
txtInput.Background = Brushes.Pink;
txtInput.Foreground = Brushes.Pink;
}
The foreground changes as expected, but the background color never changes from the default White. Am I doing something wrong? Is there some component control within ComboBox that I need to be setting properties for, as I did with TextBoxBase.TextChanged?
Set the FlatStyle attribute of the ComboBox to FlatStyle.Flat. This resolved a similar issue I experienced when the Win 7 Aero theme is turned on: The backcolor of the ComboBox does not show up in the default FlatStyle setting FlatStyle.Standard.
I had a similar issue and was able to resolve it by auto-generating the template for the ComboBox in Visual Studio 2015 (Right Click ComboBox in Design Window -> Edit Template -> Edit a Copy)

Categories