How to Hittest Grid ColumnDefinition? - wpf

I am making kind of WPF Designer. I want to find out ColumnDefinition i have clicked on to delete it from grid control. I will take care of those children who "are in that ColumnDefinition".
Can i get it from sender argument of click event handler?
Now im checking if e.GetPosition is in range of ColumnDefinition.ActualWidth but i wonder if there is more beautiful solution.

From within your click event handler:
int columnIndex = Grid.GetColumn((UIElement)sender);
where sender if a direct grid's child.

Why do you need to capture a click on ColumnDefinition anyway? Is virtual, it does not have any actual body, it is only a hint for Grid on how you want to layout its content.
So you have to set handlers on content objects, not on ColumnDefinition.
If you really need to capture a click on the whole surface of a grid cell, you may try to place a white (or other color the same as background) Reactangle inside it and capture a click on it.
Some clarification on how WPF Grid works.
When you add some controls to the Grid, they all become its children.
<Grid>
<Button/>
<TextBox/>
<Label/>
</Grid>
And they all will be displayed not regarding how you have configured Column or RowDefinitions.
Column and RowDefinitions only tell Grid how you want to aling all the existing elements inside it, but they are not containers, they don't hold elements inside.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button/><!-- this is identical to Grid.Column="0"-->
<TextBox Grid.Column="1"/>
<Label Grid.Column="2"/>
</Grid>
In this example we have created three ColumnDefinitions, even from the grid XAML you can see, that controls are not inside definitions. They are used just like ruler guides to align content.
Then you set attached properties on the elements to tell the grid where you want to put your elements.
When grid begins layout, it will see, that there are three elements, and three ColumnDefinitions, and will try to positions elements as ColumnDefinitions says.
But if you remove or change ColumnDefinitions in the runtime, grid will just realign controls in a new way.
If you want to hide some elements, you have to hide them, not ColumnDefinition.

Related

Difference between GridView and Grid

Can anyone tell me the difference between GridView and a Grid in WPF XAML?
Here are the details for UWP. Should be similar for WPF I think.
Grid - used for defining layouts and formatting or static information. It is one of the several "layout panels" that are available (others include: RelativePanel, StackPanel, VariableSizedWrapGrid, and Canvas). Grid does not have an ItemSource member to dynamically display items by binding. Grid does have Grid.Row and Grid.Column attached properties (i.e. that can be used on other controls) to position them within the Grid.
Sample Code:
<Grid x:Name="LayoutPanel1" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Margin="20"
BorderBrush="{StaticResource Page_Brush}"
BorderThickness="1 1 1 1">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="44"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
More Information: Grid Class, Layout Panels
GridView - used for displaying a set or collection of data (i.e. dynamic number of items). Another control available to display a set or collection of data is a ListView. One way to use this is by setting ItemSource (i.e. binding). By default, a data item is displayed in the GridView as the string representation of the data object it's bound to. To specify exactly how items in the GridView are displayed, you create a DataTemplate to define the layout of controls used to display an individual item. The controls in the layout can be bound to properties of a data object, or have content defined inline. You assign the DataTemplate to the ItemTemplate property of the GridView. The DataTemplate can contain a Grid (or any of the other layout panels mentioned above) to specify the layout of controls of an individual item.
Sample Code:
<GridView ItemsSource="{x:Bind MyItems}"
IsItemClickEnabled="True"
ItemClick="GridView_ItemClick"
ItemTemplate="{StaticResource MyItemTemplate}"
BorderBrush="{StaticResource MyItemBrush}"
BorderThickness="1 1 1 1"
HorizontalAlignment="Stretch"
/>
More Information: GridView Class, List view and Grid view, Guidelines for list view and grid view
A simple explanation would be
Grid
If you have just a single item with no repetitive subitem design then a grid is used. If the number of subitems are fixed
GridView
If you have a repetitive design like collection and you dont know the number of items that can be present then a gridview is used instead.
You can find more details on msdn forums.
From what I see Grid is more like a table, each row contains the same number of items (one for each column) no matter the size of the window.
GridView looks like a table but if you reduce the width of the window, the items from one row will jump on the next row:

Control the width of child in Stackpanel

I'm trying to implement a certain layout.
I have two elements that I want to stack vertically (I need them to follow each other closely). I am currently trying to achieve it using a Stackpanel.
The problem is that I want the first element to have a limited width and the other to use all the width available in the StackPanel. Ideally, I would like that the first element have a width equals to the width of four columns from the grid that contains the StackPanel, here is my code.
<Grid>
<!-- Colums and Rows definition go here -->
<StackPanel Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="3" Grid.RowSpan="8">
//The first element
<Viewbox Name="viewbox_choix" Margin="160,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="0" Grid.ColumnSpan="4" Grid.Row="3" Grid.RowSpan="4">
//The second element
<StackPanel Grid.Column="0" Grid.ColumnSpan="5">
<Border></Border>
etc...
</StackPanel>
</StackPanel>
</Grid>
The grid attributes are referring to the parent grid of the stackpanel. But the Grid.Column and Grid.ColumnSpans seem to have no effect when I try to use them inside the StackPanel.
The problem of that code is that the first element also uses all the width of the StackPanel but that isn't what I want...
Can anybody help me ? I precise that I'm still learning WPF and I don't really know how bindings work...
In WPF, a StackPanel does not work like a Grid. There is no maximum width... it will happily let content disappear out of its right side. If you want automatic resizing, just replace the StackPanels with `Grid
UPDATE >>>
In the Grid class, there is an attached property called IsSharedSizeScope. Add this to the parent Grid and set it to true. Then in your RowDefinitions, you can add SharedSizeGroup properties to the columns that you require.
These examples may help you:
Grid's SharedSizeGroup and * sizing (SO post)
Grid.IsSharedSizeScope Attached Property (MSDN)
You may need to experiment a bit, but you should be able to get the desired effect using these properties.

How to add wpf control to particular grid row and cell during runtime?

I have the following grid in my WPF "Window" (yes the class Window);
<Grid Name="RequiredGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
Depending on whats passed into the window, I want to add items into this grid one row at a time. Namely, I want to add a Label in the left column and a TextBox in the right column. I believe I know how to add new rows for holding new data by doing the following in the code behind:
RequiredGrid.RowDefinitions.Add(new RowDefinition());
Problem is, after I've created my Label and my TextBox.
Label AttrLabel = new Label();
TextBox AttrTextBox = new TextBox();
I don't really know how to get it into the Window so it gets displayed. I've seen some threads that say, do something like this:
this.Controls.Add(AttrLabel);
this.Controls.Add(AttrTextBox);
There are two problems with this. 1) My Window class doesn't have this "Controls" property or whatever. And 2) This wouldn't help me specify the row and column of each UI item.
Now in XAML, Id be easy to specify the row and column with something like this:
<Label Grid.Column="0" Grid.Row="0"/>
This defeats the "dynamic-ness" of my intent here though. Does anyone know how I can get my dynamicaly created UI elements to display in my Window and specify which row and column it will show up in the grid.
The Grid.Row and Grid.Column properties are Attached Properties, and as such are not set like normal .net properties. The right way to set them from code is:
Grid.SetRow(someLabel, 0);
Grid.SetColumn(someLabel, 0);
You should be able to do this before or after adding them to the Grid object's Children collection, but setting them before adding the control should prevent any possible flickering.
Create the grid (<yourGrid>) and the Row Definitions like you have done.
Create The Control (<yourcontrol>). Then Set The ColumnSpan and Row for the Grid:
Grid.SetColumnSpan(<yourControl>, 3);
Grid.SetRow(<yourControl>, 0);
Then add your control to the grid you have created
<yourGrid>.Children.Add(<yourControl>);

Silverlight Grid inside of TabItem header

My custom TabItem header looks like this:
<sdk:TabControl>
<sdk:TabItem >
<sdk:TabItem.Header>
<Grid Background="Gray" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<!-- some labels go here -->
</Grid>
</sdk:TabItem.Header>
</sdk:TabItem>
</sdk:TabControl>
This creates a single-row, two-column grid inside of the TabItem header. The grid automatically sizes to fit the labels, like it should, but when the size of the actual tab button grows, the grid does not adjust to fill the space (even though I specifiy <HorizontalAlignment="Stretch">).
Why is this? Is there a way to have the grid take up all available horizontal space in the header?
The problem is that the default template for a TabItem which is used to render the tab places the content of the header in a ContentControl. Now a ContentControl has the properties HorizontalContentAlignment and VerticalContentAlignment that have the default values of "Left" and "Top". This is why your grid only occupies the space it needs rather than stretching to the full size available.
In order to avoid this you will need to make a copy of the default template for the TabItem and assign the value "Stretch" to both of those properties on the ContentControl elements in the template (there are 8 in all, 2 for each possible TabStrip placement (Top, Left, Bottom and Right) ).

How to toggle a WPF Grid column visibility

I'm having some trouble getting this to work in a WPF app I'm working on. Basically, what I'm after is something like the Task pane in an MMC:
The app has three columns in the main part of the display. I need a column on the right side which is resizable. I presume this means using a Grid with a GridSplitter but anything that works will do.
I want to be able to save the width of the right-side column when the app is closed and load it when the app is opened but this should be an initial size: the user should be able to resize it.
When I resize the window, I want the left- and right-side columns to stay the same size and the middle column to resize with the window width.
The left- and right-side columns need to have a minimum width. When I resize the right-side column I want the centre column to get smaller but not the left-side column.
I also want to be able to toggle the visibility of the right-side column with a toggle button which is outside the column and when it returns to visibility I want it to be the same width it was before.
I'm trying to do as much as possible in XAML and with binding.
And can I have it topped with cream, ice cream and chocolate chips, please? :-)
As I read your requirements, instead of thinking of a Grid, I think of a DockPanel.
<DockPanel>
<Grid Name="right"
DockPanel.Dock="Right" MinWidth="100" />
<Grid Name="Left"
DockPanel.Dock="Left" MinWidth="100" />
<Grid Name="middle" />
</DockPanel>
If you make a way to resize right, then middle will change as right is resized. If you resize the window, only middle will change. Storing and setting the Width of right is up to you, but shouldn't be hard.
As for allowing the user to resize right, that will a bit trickier, but I found this article that should help. This other article might help even more.
For the visibility of right, you can set its Visibility to Collapsed to hide it and restore it by setting it to Visible.
Note: The panels inside don't have to be Grids, but you will want to use some sort of Panel for each. Whatever you have inside your current Grid columns should work just fine.
I used a Grid with GridSplitters since this made it really easy to resize the middle column while maintaining the widths of the left and right columns.
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MainWindow"
Title="Main Window"
Width="640" Height="480">
<Grid>
<Grid.ColumnDefinitions>
<!-- Left column -->
<ColumnDefinition Width="200" MinWidth="100"/>
<!-- Left GridSplitter column -->
<ColumnDefinition Width="5"/>
<!-- Center column. A width of * means the column will fill
any remaining space. -->
<ColumnDefinition Width="*"/>
<!-- Right GridSplitter column -->
<ColumnDefinition x:Name="RightSplitterColumn" Width="5"/>
<!-- Right column -->
<ColumnDefinition x:Name="RightColumn" Width="200"
MinWidth="100"/>
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
<GridSplitter Grid.Column="3" HorizontalAlignment="Stretch" />
<Button x:Name="ToggleButton" Grid.Column="2"
Content="Toggle Right Column" Width="150" Height="25"
Click="ToggleButton_Click" />
</Grid>
</Window>
Code-Behind
When hiding the right column, I just set the column width to 0 since grid columns don't have a visibility property.
public partial class MainWindow : Window
{
private double rightColumnWidth;
private double rightColumnMinWidth;
private bool rightColumnHidden;
public MainWindow()
{
this.InitializeComponent();
}
private void ToggleButton_Click(object sender, RoutedEventArgs e)
{
if (rightColumnHidden)
{
// Restore the widths.
RightColumn.MinWidth = rightColumnMinWidth;
RightColumn.Width = new GridLength(rightColumnWidth);
RightSplitterColumn.Width = new GridLength(5);
}
else
{
// Remember the user-set widths for the columns.
rightColumnWidth = RightColumn.Width.Value;
rightColumnMinWidth = RightColumn.MinWidth;
// Remember to set the minimum width to 0 before changing the actual
// width.
RightColumn.MinWidth = 0;
RightColumn.Width = new GridLength(0);
RightSplitterColumn.Width = new GridLength(0);
}
rightColumnHidden = !rightColumnHidden;
}
}
As for saving and restoring the column widths on startup, I would just store the width variables to a settings file and then apply them when your app is reopened.
Set the columndefinition Width to Auto and put a control inside that column and give Star for the other columns . Whenever you want to hide the column with content, set the control.Visibility=Collapsed and since column width is Auto, you wont see that column and the remaining columns will take the space.
3 years later you can find another approach on CodeProject.
http://www.codeproject.com/Articles/437237/WPF-Grid-Column-and-Row-Hiding
It adds a "Visible" property to custom Column definitions.

Resources