I have a MVVM application. In main window I have put below grid with 2 columns. In column 2 I have placed an WPF expander and I set it as collapsed by default (IsExpanded="False"). When application executes I would like grid column 1 to fill the entire width of the grid and grid column 2 with width 0 (collapsed). So when I click in the expander I want grid column 2 to expand to take 0.47* of width. Then if I click the expander again, I would like grid column 1 to fill the entire width of the grid and grid column 2 to be collapsed (width=0). How can I do this? Below code is not working.
<Grid x:Name="Grid">
<Grid.RowDefinitions>
<RowDefinition Height="47.5*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="0.47*" />
</Grid.ColumnDefinitions>
<controls:UCIndicationsOfUse Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="auto"
Height="auto"
DataContext="{Binding}" />
<controls:UCPData Grid.Row="1" Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="auto"
Height="auto"
DataContext="{Binding}" />
<Expander Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Width="25"
ExpandDirection="Left"
IsExpanded="False"
HorizontalAlignment="Right">
<Expander.Header>
<TextBlock Text="Settings">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Expander.Header>
</Expander>
Finally I have added Collapsed and Expanded events to the expander:
<Expander Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="1"
Width="25"
Collapsed="Expander_Collapsed"
Expanded="Expander_Expanded"
ExpandDirection="Left"
IsExpanded="False"
HorizontalAlignment="Right">
<Expander.Header>
<TextBlock Text="Settings">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90"/>
</TextBlock.LayoutTransform>
</TextBlock>
</Expander.Header>
</Expander>
And then from view code-behind:
private void Expander_Collapsed(object sender, RoutedEventArgs e)
{
ColumnDefinition c = this.Grid.ColumnDefinitions[1];
c.Width = new GridLength(0, GridUnitType.Auto);
this.Grid.ColumnDefinitions.RemoveAt(1);
this.Grid.ColumnDefinitions.Insert(1, c);
}
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
ColumnDefinition c = this.Grid.ColumnDefinitions[1];
c.Width = new GridLength(0.47, GridUnitType.Star);
this.Grid.ColumnDefinitions.RemoveAt(1);
this.Grid.ColumnDefinitions.Insert(1, c);
}
I think this not break MVVM pattern since it is a task for the view.
Related
I am currently fighting against another WPF struggle, namely mouse events.
I basically have a very simple control (a Border containing a Grid which itself has a few TextBlocks). I am trying to achieve a simple behavior: Double click should turn the control into edit mode (which in fact hides the TextBlocks with TextBoxes bound to the same data.
Nothing fancy, right? Well still, I'm struggling. The MouseDoubleClick linked to my UserControl just fires when I click in a control (like, clicking ON a TextBlock). If I click on an empty space between TextBlocks, nothing is fired. Not even MouseDown.
How could I make it work so as to catch every mouse click? I assumed that linking a MouseDown event to a Border should catch every click on the border but... it ended up not catching clicks on empty parts of the border.
Here is some draft code I made for you to run it:
XAML:
<StackPanel Orientation="Vertical">
<Border Height="400" Width="400" HorizontalAlignment="Center" VerticalAlignment="Center" BorderBrush="Black" BorderThickness="2"
MouseDown="Border_MouseDown" MouseUp="Border_mouseUp">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="BLUFF" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="BLUFF" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="BLUFF" Grid.Column="0" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="BLUFF" Grid.Column="1" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="BLUFF" Grid.Column="1" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="BLUFF" Grid.Column="1" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="BLUFF" Grid.Column="2" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="BLUFF" Grid.Column="2" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="BLUFF" Grid.Column="2" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Border>
<TextBlock Height="100" Width="300" HorizontalAlignment="Center" TextAlignment="Center" x:Name="thetext" Visibility="Collapsed"
Foreground="White" Background="Red" Text="CLICKED!" />
</StackPanel>
Code behind:
private void Border_MouseDown(object sender, MouseButtonEventArgs e)
{
thetext.Visibility = Visibility.Visible;
}
private void Border_mouseUp(object sender, MouseButtonEventArgs e)
{
thetext.Visibility = Visibility.Collapsed;
}
Now try to click one of the "BLUFF" texts: A "CLICKED" Text will appear. Try clicking somewhere else, between the TextBlocks: Nothing happens.
Thank you!
<StackPanel Background="Transparent" ... >
or
<Border Background="Transparent" ... >
should do the trick...
This makes the grid transparent, however visible for mouse-clicks.
Look here form more Information.
There are some other circumstances, where you have to use the PreviewXXX- event, this is when a child element "swallows" the event away, but in your case, the above should be what you're looking for.
I'm trying to layout a set of TextBoxes, vertically, one on top of the other. If there are so many TextBoxes that they cannot fit within the Window height then I want a ScrollBar to become available on the Window. The number of input TextBoxes is variable. There is always a last TextBox with the name output.
If all the TextBoxes can appear within the Window height then I want the last TextBox to stretch to the bottom and fill all the remaining space. In addition, if the output TextBox has a lot of Text then I want this TextBox to make a ScrollBar available.
Here's some pseudo XAML of what I have:
<ScrollViewer>
<Grid>
<TextBox Name="input1" Margin="12, 12, 12, 12" VerticalAlignment="Top" HorizontalAlignment="Left" />
<TextBox Name="input2" Margin="12, 60, 12, 12" VerticalAlignment="Top" HorizontalAlignment="Left" />
<TextBox Name="input3" Margin="12, 108, 12, 12" VerticalAlignment="Top" HorizontalAlignment="Left" />
...
<TextBox Name="inputN" Margin="12, ((N - 1) * 48 + 12), 12, 12" VerticalAlignment="Top" HorizontalAlignment="Left" />
<TextBox Name="ouput" Margin="12, (N * 48 + 12), 12, 12" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
</Grid>
</ScrollViewer>
The problem I have right now is that if the output TextBox has a lot of content then it increases in height and has the Window present a ScrollBar. Instead I want the output TextBox to get the ScrollBar.
I can fix that problem by removing the ScrollViewer that is holding the Grid. However if I do that I get another problem. Because the number of input TextBoxes is variable, if there are so many that not all the TextBoxes can show in the Window, the Window doesn't get a ScrollBar, which I need.
I have tried playing around with putting a MaxHeight on the output TextBox, but if there are few input TextBoxes and a tall Window then I'm apt to not fill all of the remaining vertical space.
It sounds like you need to wrap your Output TextBox in a ScrollViewer and also assure it does stay in the bottom (defining some rows in the Grid or docking it to the bottom of a DockPanel.
EDIT: Do you need something like this?
<Grid x:Name="grd01">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="1">
<TextBox HorizontalAlignment="Stretch" TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
SECOND EDIT:
<Grid x:Name="grd01">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0">
<StackPanel>
<TextBox x:Name="input"
HorizontalAlignment="Stretch"
TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<ScrollViewer Grid.Row="1">
<TextBox x:Name="output"
HorizontalAlignment="Stretch"
TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
you can use this tweak on the window loaded event with this code:
EDIT:
i modified it so it's even correct if resizing now
private void Window_Loaded(object sender, RoutedEventArgs e)
{
output.MaxHeight = output.ActualHeight;
output.VerticalAlignment = VerticalAlignment.Top;
output.Height = output.MaxHeight;
}
private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
output.MaxHeight = output.MinHeight = output.Height = outGrid.ActualHeight;
}
-
<ScrollViewer Height="Auto">
<DockPanel>
<StackPanel DockPanel.Dock="Top" >
<TextBox VerticalAlignment="Top" HorizontalAlignment="Stretch" />
<TextBox VerticalAlignment="Top" HorizontalAlignment="Stretch" />
<TextBox VerticalAlignment="Top" HorizontalAlignment="Stretch" />
<TextBox VerticalAlignment="Top" HorizontalAlignment="Stretch" />
<TextBox VerticalAlignment="Top" HorizontalAlignment="Stretch" />
<TextBox VerticalAlignment="Top" HorizontalAlignment="Stretch" />
</StackPanel>
<Grid DockPanel.Dock="Bottom" Name="outGrid">
<TextBox Name="output" TextWrapping="Wrap" AcceptsReturn="True" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto" VerticalAlignment="Stretch" />
</Grid>
</DockPanel>
</ScrollViewer>
I'm trying to make a list that is populated by external data and I have a datatemplate for it.
Each element of the list is composed of an image and two textblocks, and is linked to a specific page.
I'm trying to do this but when I wrap the structure with an HyperlinkButton, I just get a blank page.
I don't know if I'm doing something stupid wrong or it's not possible to have so many items in an HyperlinkButton. If it's not possible to do it this way, can someone guide me to the best solution to do this? Here's the code:
<Grid x:Name="ContentPanel" Margin="0,140,0,0" Background="White">
<ListBox HorizontalAlignment="Stretch" Name="itemList" VerticalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<HyperlinkButton>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="480">
<Grid.Background>
<ImageBrush ImageSource="/app;component/Images/degradat_cela.png" Stretch="UniformToFill" AlignmentY="Top" AlignmentX="Left" />
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="430*" />
</Grid.ColumnDefinitions>
<Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="38" Width="38" Grid.RowSpan="2" Grid.Column="0" Grid.Row="0" BorderThickness="1" BorderBrush="#FFFF003F" Padding="1">
<Image HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Name="listImage" Width="36" Height="36" Source="{Binding image}" />
</Border>
<TextBlock Margin="5 12 0 0" Grid.Column="1" Grid.Row="0" Name="title" Foreground="Black" Text="{Binding title}" FontWeight="Bold" FontSize="18" />
<TextBlock Margin="5 0 0 8" Grid.Column="1" Grid.Row="1" Name="description" Foreground="Black" Text="{Binding subtitle}" FontSize="14" />
</Grid>
</HyperlinkButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I will also accept any suggestions to make my code better, as I'm new to .NET and I probably don't do things the best way!
Remove the HyperlinkButton and instead use the SelectionChanged event of the ListBox. So add this property to your ListBox:
SelectionChanged="myListBox_SelectionChanged"
Then in your code behind do this:
private void myListBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if ((sender as ListBox).SelectedIndex == -1)
return;
NavigationService.Navigate(new System.Uri(string.Format("/Drilldown.xaml?Index={0}",(sender as ListBox).SelectedIndex),System.UriKind.Relative));
}
This code assumes a drilldown page that uses a query string to change it's layout. This is just for example. If instead of index you wanted to reference some property of the bound item you could instead do something like this (assuming an ID property):
int itemID = ((sender as ListBox).SelectedItem as MyApp.Model.myItem).ID;
NavigationService.Navigate(new System.Uri(string.Format("/Drilldown.xaml?ID={0}",itemID),System.UriKind.Relative));
I create a GridSplitter across the 3 rows that I have in my grid like this:
<GridSplitter Grid.Row="0" Grid.Column="1" Background="Yellow"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Width="Auto" Height="Auto" ResizeDirection="Columns"
Grid.RowSpan="3" ...
However, it's conceivable that I might add another row to my grid at a later stage, and I don't really want to go back and update all of my rowspans.
My first guess was Grid.RowSpan="*", but that doesn't compile.
A simple solution:
<!-- RowSpan == Int32.MaxValue -->
<GridSplitter Grid.Row="0"
Grid.Column="1"
Grid.RowSpan="2147483647" />
You can bind to the RowDefinitions.Count but would need to update the binding when adding rows manually.
Edit: Only semi-manually in fact
Xaml:
<StackPanel Orientation="Vertical">
<Grid Name="GridThing">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.Children>
<Button Content="TopRight" Grid.Row="0" Grid.Column="1"/>
<Button Content="LowerRight" Grid.Row="1" Grid.Column="1"/>
<Button Content="Span Rows" Name="BSpan" Grid.RowSpan="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=RowDefinitions.Count, Mode=OneWay}"/>
</Grid.Children>
</Grid>
<Button Click="Button_Click" Content="Add Row" />
</StackPanel>
Code:
private void Button_Click(object sender, RoutedEventArgs e)
{
GridThing.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(20) });
foreach (FrameworkElement child in GridThing.Children)
{
BindingExpression exp = child.GetBindingExpression(Grid.RowSpanProperty);
if (exp != null)
{
exp.UpdateTarget();
}
}
}
The Grid control provides nothing like this out of the box. It's conceivable that you could implement a MarkupExtension or some other trickery to enable this.
I have expander control in grid row. And I change the expander size on the expander button click to maximize the expander size. And on the collapse status I minimize the expander size. The problem is the expander expands under the grid row.
Is there is any way to make the expander expand on top of any control?
<Grid AllowDrop="False">
<Grid.RowDefinitions>
<RowDefinition Height="199*" />
<RowDefinition Height="175*" />
</Grid.RowDefinitions>
<Grid Height="60" HorizontalAlignment="Left" Margin="21,26,0,0" Name="grid1" VerticalAlignment="Top" Width="550" Background="#FFE59E9E">
<Label Content="Label" Height="28" HorizontalAlignment="Left" Margin="62,113,0,0" Name="label1" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="130,115,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" Background="#FF2B1313" />
</Grid>
<Expander Name="exp" Expanded="exp_Expanded" Background="#FF3383A7" BorderThickness="4" BorderBrush="{x:Null}" FlowDirection="LeftToRight" Collapsed="exp_Collapsed" ExpandDirection="Left" Height="49" VerticalAlignment="Top" HorizontalAlignment="Right" Width="602" Margin="0,139,237,0">
<DataGrid AutoGenerateColumns="False" Height="105" Name="dataGrid1" Width="200" HorizontalContentAlignment="Center" VerticalAlignment="Center" />
</Expander>
</Grid>
private void exp_Expanded(object sender, RoutedEventArgs e)
{
var exp = (Expander) sender;
//grid1.Width = 550;
// grid1.Height = 40;
exp.Width = 602;
exp.Height = 300;
}
private void exp_Collapsed(object sender, RoutedEventArgs e)
{
var exp = (Expander)sender;
// grid1.Height = 500;
exp.Width = 602;
exp.Height = 49;
}
This is because you have set Grid.RowDefinition for you Grid control and didnt set the Grid.Row property for child controls
if not required remove the below code in you xaml
<Grid.RowDefinitions>
<RowDefinition Height="199*" />
<RowDefinition Height="175*" />
</Grid.RowDefinitions>
or
Add Grid.RowSpan property for your expander
<Expander Grid.RowSpan="2" Name="exp" Expanded="exp_Expanded"...
You can check WPF Tutorial for Grid Panel for more details on how to rows and columns for Grid Panel