WPF (VS2010/.NET4.0) Create a re-usable form layout - wpf

In a nutshell what Im trying to achieve is to have a re-usable DLL which will potentially have a wizard like form. I could then simply set the content. Ive spent quite a bit of time searching but Im still not sure whats the best way to go. Ive had a look at this article as well.
Ive got the following structure in the XAML code:
<Grid x:Name="MainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="30"/>
<RowDefinition Height="20"/>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Content="{Binding ScreenTitleText}" />
<Label x:Name="ContentTitle" Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="2" Content="{Binding ContentTitleText}" />
<Button x:Name="BackButton" Grid.Row="5" Grid.Column="1" Content="Back" />
<Button x:Name="NextButton" Grid.Row="5" Grid.Column="3" Content="Next" />
<ScrollViewer Grid.Row="4" Grid.Column="2" Content="{Binding InnerContent}" x:Name="InnerControl"/>
</Grid>
Id like to know how to make it so that I could set the content on row=4 and column=2 to say for example a set of radio buttons.
How to have this code in a DLL so that I could re-use it.
Thanks!

Create this as a WPF User Control in a Class Library or a WPF User Control Library. Then, put ContentControls where you want the dynamic stuff to go. You can expose DataTemplate properties for each of those ContentControls. The ContentControls can bind their Template to a DataTemplate and you should be good to go.

Related

Unexpected auto grid column width

the code below is a small snippet of a big wpf Window I am using in my Project. It produces the linked wpf Window.
I am wondering why my last grid column is so wide. I am expecting that the width of the last column depends on the width of the button, because the column width is set to "Auto".
If I remove the columnspan of the StackPanel the column width will be correct but then the CheckBoxes are not aligned to right side.
I hope you have understood my problem. My aim is, that the last column is as small as possible, the checkboxes are at the right side and the rest stays at it is.
Because this snippet is part of a bigger wpf window I cannot remove any grid rows or columns.
Thank you very much for your help.
Best regards.
WPF Window
<Window x:Class="TestProject.Window1"
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"
d:DesignHeight="152.429" d:DesignWidth="403">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Grid.Row="0"
Margin="5"
Grid.ColumnSpan="2"/>
<Button Grid.Column="2"
Grid.Row="0"
Margin="5"
Width="40"/>
<ComboBox Grid.Column="0"
Grid.Row="1"
Margin="5"
Grid.ColumnSpan="3"/>
<Image Grid.Column="0"
Grid.Row="2"/>
<StackPanel Grid.Column="1"
Grid.Row="2"
Grid.ColumnSpan="2">
<CheckBox Margin="5"
Content="checkbox content 1"/>
<CheckBox Margin="5,0,5,5"
Content="checkbox content 2"/>
</StackPanel>
</Grid>
You can put a grid inside another grid.
Here is the code that will help you achieve your goal.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0"
Grid.Row="0"
Margin="5"
Grid.ColumnSpan="2"/>
<Button Grid.Column="2"
Grid.Row="0"
Margin="5"
Width="40"/>
<ComboBox Grid.Column="0"
Grid.Row="1"
Margin="5"
Grid.ColumnSpan="3"/>
<Grid Name="GridInsideAGrid"
Grid.Column="0"
Grid.Row="2"
Grid.ColumnSpan="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" />
<StackPanel Grid.Column="1">
<CheckBox Margin="5"
Content="checkbox content 1"/>
<CheckBox Margin="5,0,5,5"
Content="checkbox content 2"/>
</StackPanel>
</Grid>
</Grid>

How do I retrieve column width in a WPF Grid using a GridSplitter?

I need to display to the user two listboxes - one on either side of the window - and allow the user to choose how much screen space is devoted to each. I have achieved that much with the following code:
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding FirstColumnWidth}" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListBox Name="FirstColumn" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" Grid.Column="0" />
<GridSplitter Name="gridSplitter1" Width="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" Grid.Column="1" />
<ListBox Name="SecondColumn" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="0" Grid.Column="2" />
</Grid>
However, whilst the binding will retrieve an initial width for the first column and size them both accordingly, using the GridSplitter to resize them simply replaces the binding with the new value. How can I retrieve the new value so that I can persist it?
Ideally, the solution needs to play nicely with MVVM - I'm using Caliburn and trying to keep the code as clean as possible (my view model contains the FirstColumnWidth property that is currently being bound).
There are two solutions that I can think of.
1. Use the different binding modes available and the Width and ActualWidth properties.
Example
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding Path=FirstColumnWidth, Mode=OneTime}" />
<ColumnDefinition Width="3" />
<ColumnDefinition Width="{Binding Path=SecondColumnWidth, Mode=OneTime}" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Column="0" ActualWidth="{Binding Path=FirstColumnWidth, Mode=OneWayToSource}">
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
<GridSplitter Grid.Column="1" Width="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<Grid Grid.Column="2" ActualWidth="{Binding Path=SecondColumnWidth, Mode=OneWayToSource}">
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
</Grid>
or
2. Override the current/default behaviour of the grid splitter using the attached behaviour pattern (tutorial on CodeProject). I will post of code sample for this tomorrow when I'm at my desk.

Silverlight Grid does not fill

I have Border control defined like so:
<Border Background="Azure" Grid.Row="2">
<ContentControl Width="Auto" Height="Auto" Regions:RegionManager.RegionName="MainContent" />
</Border>
I can see Azure background in whole area
Now I inject my view into this ContentControl (it's PRISM). View looks like this..
<toolkit:BusyIndicator IsBusy="{Binding IsBusy}">
<Grid Margin="10" DataContext="{Binding}"
infBehaviors:RegionPopupBehaviors.CreatePopupRegionWithName="ViewPopup"
infBehaviors:RegionPopupBehaviors.ContainerWindowStyle="{StaticResource PopupStyle}">
<!--Define rows in a grid-->
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!--Define columns in a grid-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="65" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
Now when I place new UserCOntrol on top of my Grid - I expect it to cover whole "Azure" area. But I only see overlay with size of my data entry form. It seems that second grid does not "fill" ContentControl - only takes as much space as needed. How do I force it to fill? I set Auto column and row - thinking they will stretch but no..
EDIT:
Screenshot from Silverlight Spy.. It shows that ContentControl from Shell covers whole area but grid inside totally ignores my "*" sizes. Also it does work in design mode - it stretches to whole design area...
Make sure you have HorizontalContentAlignment and VerticalContentAlignment of the ContentControl set to Stretch. ^_^
e.g.
<Border Background="Azure" Grid.Row="2">
<ContentControl HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
Width="Auto" Height="Auto" Regions:RegionManager.RegionName="MainContent" />
</Border>

Telerik Silverlight RadPanelBar Hierarchical Data Template

I need to display the following layout with a telerik PanelBar.
With the code below I was able to achieve everything except the 92% stuff in each panel.
XAML:
<UserControl.Resources>
<DataTemplate x:Key="PanelBarItemTemplate">
<Grid x:Name="grdCategory" ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60*"></ColumnDefinition>
<ColumnDefinition Width="40*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid x:Name="grdSubCategory" Grid.Column="0" Style="{StaticResource CategoryLeftStyle}" >
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50*"></ColumnDefinition>
<ColumnDefinition Width="25*"></ColumnDefinition>
<ColumnDefinition Width="25*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding CategoryTitle}" Grid.Row="0" Grid.Column="0"/>
<HyperlinkButton Grid.Row="0" Grid.Column="1" Style="{StaticResource DetailLinkStyle}" Content="Details" Click="Home_Click"></HyperlinkButton>
<TextBlock Text="{Binding Score}" Grid.Row="0" Grid.Column="2"/>
</Grid>
<TextBlock Text="92%" Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" FontSize="32" FontWeight="Bold"/>
</Grid>
</DataTemplate>
<telerik:HierarchicalDataTemplate x:Key="PanelBarHeaderTemplate"
ItemsSource="{Binding SubReports}"
ItemTemplate="{StaticResource PanelBarItemTemplate}">
<TextBlock Text="{Binding CategoryTitle}" />
</telerik:HierarchicalDataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<telerik:RadPanelBar x:Name="radPanelBar"
ItemTemplate="{StaticResource PanelBarHeaderTemplate}"
IsSingleExpandPath="False" >
</telerik:RadPanelBar>
</Grid>
in the xaml.cs file I provided the ItemsSource.
Can somebody help me out?
All that code works perfectly well for the individual items, but to place the 92% relative to those items (somewhat outside of the sub-items) you would need to also modify the ItemContainerStyle of RadPanelBar. Easiest way is to extract it in Blend, then look for the section under PanelBarItemTopLevelTemplate named ItemsContainer. This is a somewhat crude version, but I made a public property on my item called CalcInt that calculates the sum of a property on the SubReport items so it can be bound from the base item level. My modified code looks like so:
<Grid x:Name="ItemsContainer" Grid.Row="1" Visibility="Collapsed">
<telerik:LayoutTransformControl x:Name="transformationRoot">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ItemsPresenter/>
<TextBlock Text="{Binding CalcInt}" FontSize="48" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</telerik:LayoutTransformControl>
</Grid>
I basically changed it from just containing an ItemsPresenter to a grid with some layout to display my extra-large TextBlock.
If you need a sample of the code or have any other questions feel free to hit me up on Twitter - #EvanHutnick.
Cheers!
-Evan
Hi I think you need to define a converter : IValueConverter and bind to the Label so you can calculate the percentual.
Mario

Not able to refer a control in code behind in VS 2008 with WPF

I don't know why but for some reason I am not able to refer to my tbText control in my code behind file. Here is the XAML part:
<ComboBox.ItemTemplate>
<DataTemplate>
<ItemsControl x:Name="ic">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
</Grid>
**<TextBlock x:Name="tbText" Grid.Column="0" Grid.Row="0" Margin="10" />**
<Image Grid.Column="1" Margin="10" Grid.Row="0" Width="100" Height="100" Stretch="Fill">
</Image>
</ItemsControl>
</DataTemplate>
</ComboBox.ItemTemplate>
I cannot refer to the "tbText" control.
You can't refer to it because it is inside an Items control.
You'd have to search the ItemsControl children in order to find the textbox.
See Finding control within wpf items control for ways to do this.

Resources