Draw Diagonal Line in WPF Grid - wpf

I think I am trying to do something relatively simple in WPF, but can't for the life of me figure out how; and think I am probably on the verge of overcomplicating it.
If I had a grid which was 3 rows and 3 columns, and I wanted to join the corners of two cells to create a diagonal border, what would be the best way of doing so?
The lines should ideally re-size if the control is resized (so bound to the corners of the cell?).
Essentially I would like to create the red lines in the diagram hosted here: Example Pic http://imm.io/7A4L

You could use a Path with Stretch=Fill. For the top right cell in your example, you would use:
<Path Grid.Row="2" Grid.Column="0" Stroke="Red" StrokeThickness="2" Stretch="Fill">
<Path.Data>
<LineGeometry StartPoint="0,0" EndPoint="1,1" />
</Path.Data>
</Path>
The "Fill" stretch makes the Path stretch to fill its parent, which gives the impression that the coordinates of the LineGeometry are relative (X=0,Y=0 is top left, X=1,Y=1 is bottom right).

I have created a sample to draw line from code behind which will give you more control...
I crated a grid which contains canvas in each cell and on canvas load i am creating a path and adding it to same canvas...
As # Mathieu Garstecki answer we can achieve this crating path in xaml... if want to add some logic before creating path you can use my answer
XAML
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Canvas Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="AliceBlue" Loaded="Canvas_Loaded"></Canvas>
<Canvas Grid.Row="1" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="AliceBlue" Loaded="Canvas_Loaded"></Canvas>
<Canvas Grid.Row="2" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="AliceBlue" Loaded="Canvas_Loaded"></Canvas>
<Canvas Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="AliceBlue" Loaded="Canvas_Loaded"></Canvas>
<Canvas Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="AliceBlue" Loaded="Canvas_Loaded"></Canvas>
<Canvas Grid.Row="2" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="AliceBlue" Loaded="Canvas_Loaded"></Canvas>
<Canvas Grid.Row="0" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="AliceBlue" Loaded="Canvas_Loaded"></Canvas>
<Canvas Grid.Row="1" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="AliceBlue" Loaded="Canvas_Loaded"></Canvas>
<Canvas Grid.Row="2" Grid.Column="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="AliceBlue" Loaded="Canvas_Loaded"></Canvas>
</Grid>
Code Behind
private void Canvas_Loaded(object sender, RoutedEventArgs e)
{
var g = new StreamGeometry();
var context = g.Open();
context.BeginFigure(new Point(0, 0), true, true);
context.LineTo(new Point((sender as Canvas).ActualHeight, (sender as Canvas).ActualWidth), true, true);
context.Close();
System.Windows.Shapes.Path path = new System.Windows.Shapes.Path();
path.Data = g;
path.Stroke = new SolidColorBrush(Colors.Red);
path.StrokeThickness = 1.4;
(sender as Canvas).Children.Add(path);
}
OutPut

Related

WPF: Resizing stackpanels with images at runtime

I am attempting to create an application for a flight simulator
aircraft where a user can preset the position and size of a window of
a set of popup radio displays as seen in the first screenshot.
However, I have run into a bit of a problem. In the second
screenshot, when I scale the main grid horizontally
with a grid splitter, the images increase in size vertically as well
as horizontally instead of remaining at their initial height. Any
ideas how to solve this?
Ok the solution was to use a grid and set the Grid.Row the image was assigned to. Then by scaling the grid I could get the desired effect.
XAML:
<Grid x:Name="spGrid" Height="154" Width="101" Margin="0,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" MouseDown="PosStart" MouseMove="PosDelta" MouseUp="PosEnd">
<Grid.RowDefinitions>
<RowDefinition x:Name="spRow1" Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="spCol1" Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" Grid.RowSpan="5" Width="1" HorizontalAlignment="Right" VerticalAlignment="Stretch" DragDelta="PanelXDelta" DragStarted="PanelXStart"/>
<GridSplitter Grid.Row="1" Grid.ColumnSpan="4" Height="1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" DragDelta="PanelYDelta" DragStarted="PanelYStart"/>
<Grid Grid.Column="0" Grid.Row="0" x:Name="spStack" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Image Grid.Row="0" x:Name="imgKMA30" Source="UI/KMA30.jpg" StretchDirection="Both" Stretch="Fill" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Image Grid.Row="1" x:Name="imgKR165A" Source="UI/KX165A.jpg" StretchDirection="Both" Stretch="Fill" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Image Grid.Row="2" x:Name="imgKX165" Source="UI/KX165.jpg" StretchDirection="Both" Stretch="Fill" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Image Grid.Row="3" x:Name="imgKN62A" Source="UI/KN62A.jpg" StretchDirection="Both" Stretch="Fill" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Image Grid.Row="4" x:Name="imgKR87" Source="UI/KR87.jpg" StretchDirection="Both" Stretch="Fill" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Image Grid.Row="5" x:Name="imgKT74" Source="UI/KT74.jpg" StretchDirection="Both" Stretch="Fill" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
</Grid>
C#:
private void PanelXDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
spGrid.Width += e.HorizontalChange;
}
private void PanelYDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
spGrid.Height += e.VerticalChange;
}

How to get the value of the margin of a border?

I'm a beginner in WPF and have to add functionality to someone's user interface. Here's part of the code.
<Border BorderBrush="Black" BorderThickness="1" Grid.Row="1" >
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="{Binding ElementName=passFailIndicator, Path=Width}"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border Background="Black" local:StretchPanel.Proportion="1" Name="imageBorder" Grid.Column="0">
<Border BorderThickness="1" Margin="2" BorderBrush="Green" HorizontalAlignment="Left" VerticalAlignment="Top" Width="{Binding ElementName=imageBorder, Path=Width}">
<Grid Width="{Binding ElementName=imageBorder, Path=Width}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="{Binding ElementName=imageSelectExpander, Path=Width}"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" x:Name="image" Source="{Binding DisplayImage, Mode=OneWay}" VerticalAlignment="Top" Stretch="Uniform" HorizontalAlignment="Left" StretchDirection="Both" MouseMove="image_MouseMove" />
<TextBlock Name="pxPos" Text="mouse position" HorizontalAlignment="Right" VerticalAlignment="Bottom" MaxHeight="20" Foreground="Aqua"></TextBlock>
<Expander Grid.Column="1" VerticalAlignment="Top" Name="imageSelectExpander">
<ComboBox x:Name="imageSelect" ItemsSource="{Binding AvailableImages, Mode=OneWay}" SelectedIndex="{Binding ImageSelect, Mode=TwoWay}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Margin="4" Padding="4" MaxHeight="40"></ComboBox>
</Expander>
</Grid>
</Border>
</Border>
<Border x:Name="passFailIndicator" Grid.Column="1" HorizontalAlignment="Left" VerticalAlignment="Center" Width="100" Height="100">
<Viewbox Stretch="Uniform" >
<Path Style="{Binding TestResult, Mode=OneWay, Converter={StaticResource testResultToPathStyle}}" Margin="2" />
</Viewbox>
</Border>
</Grid>
</Border>
I'm trying to access the Margin="2" on line 10. When I use this.imageBorder.Margin, I get {0,0,0,0}. How would I get the 2 (of course, this value may change)?
thanks
edit:
FrameworkElement fe = (FrameworkElement)this.imageBorder.Child;
pxPos.Text = (string.Format("x:{0} y:{1}", (int)((double)(pt.X - fe.Margin.Left) * (double)this.image.Source.Width / this.image.ActualWidth), (int)((double)(pt.Y - fe.Margin.Top)*(double)this.image.Source.Height / this.image.ActualHeight)));
The Border labeled imageBorder does not have its Margin property set to anything, so you are getting the default Margin, which is 0.
To get the Margin property of the Border inside your named border, either give it an x:Name so you can access it in code behind, or look in imageBorder.Child property to find the child Border object, then cast it to a FrameworkElement to get its Margin property.
As for why you're getting {0,0,0,0}, the Margin property is of type Thickness, which consists of properties for Left, Top, Right, and Bottom. If you set a margin to a single value, such as 2, it automatically converts that to a Thickness object with all 4 of its properties set to 2.

Gridsplitter behaviour when hiding a WPF grid column

I'm pretty new to WPF, so please excuse me if this is 'old hat' these days... have trawled the web/forum and haven't quite found the answer I need:
I have a WPF grid with 5 columns - three for data, two for gridsplitters, which (thanks to info on this site!) seems to work and resize fine. However - I need to be able to show/hide the middle column. I can sort-of do this, but when I hide the middle column, the left hand gridsplitter still affects the "hidden" column - I need to effectively toggle between 2 & three colums. Here's my (prototype) code:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Name="Col0" Width="*" />
<ColumnDefinition Name="Col1" Width="auto" />
<ColumnDefinition Name="Col2" Width="*" />
<ColumnDefinition Name="Col3" Width="auto" />
<ColumnDefinition Name="Col4" Width="auto" />
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" Height="100" HorizontalAlignment="Center" Margin="0" Name="GridSplitter1" VerticalAlignment="Stretch" Width="3" />
<GridSplitter Grid.Column="3" Height="100" HorizontalAlignment="Center" Margin="0" Name="GridSplitter2" VerticalAlignment="Stretch" Width="3" />
<Border BorderBrush="Silver" BorderThickness="1" Grid.Column="0" HorizontalAlignment="Stretch" Margin="0" Name="Border1" VerticalAlignment="Stretch" Background="#FFC84797" />
<Border BorderBrush="Silver" BorderThickness="1" Grid.Column="2" HorizontalAlignment="Stretch" Margin="0" Name="Border2" VerticalAlignment="Stretch" Background="Black" />
<Border BorderBrush="Silver" BorderThickness="1" Grid.Column="4" HorizontalAlignment="Stretch" Margin="0" Name="Border3" VerticalAlignment="Stretch" Background="#FFA60000">
<Button Content="hide" Height="33" Name="butHide" Width="85" />
</Border>
</Grid>
Private Sub butHide_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles butHide.Click
If butHide.Content = "hide" Then
butHide.Content = "show"
Col2.Width = New GridLength(0)
Border2.Visibility = System.Windows.Visibility.Collapsed
GridSplitter2.Visibility = System.Windows.Visibility.Collapsed
Else()
butHide.Content = "hide"
Col2.Width = New GridLength(1, GridUnitType.Star)
Border2.Visibility = System.Windows.Visibility.Visible
GridSplitter2.Visibility = System.Windows.Visibility.Visible
End If
End Sub
Probably the easiest thing for you here is just to set Grid.ZIndex="2" for Border1 and then toggle the ColumnSpan between 1 and 3 in the click event.
<Border Grid.Column="0"
Grid.ZIndex="2"
Name="Border1"
.../>
Code behind
private void butHide_Click(object sender, RoutedEventArgs e)
{
if (butHide.Content.ToString() == "hide")
{
butHide.Content = "show";
Grid.SetColumnSpan(Border1, 3);
}
else
{
butHide.Content = "hide";
Grid.SetColumnSpan(Border1, 1);
}
}
The other solution is put the grid splitter in the same column you want to resize behind its content and set margin to the content. See this link for reference solution: http://www.ehow.com/how_4546867_use-gridsplitter-wpf.html
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<GridSplitter Grid.Row="0" Grid.Column="0" HorizontalAlignment="Right" Width="4" Background="Yellow"/>
<TextBlock Grid.Row="0" Grid.Column="0" Margin="0 0 4 0" Background="LightGray">Text Block</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Background="LightGreen">Text Block 2</TextBlock>
</Grid>
Read more: How to Use a Gridsplitter in WPF | eHow.com http://www.ehow.com/how_4546867_use-gridsplitter-wpf.html#ixzz1mXqi6sGa

Windows Phone 7 Silverlight DataTemplate HyperlinkButton content

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));

Is there a RowSpan="All" in WPF?

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.

Resources