How to group controls inside Grid for reuse in XAML? - c#

In my application I have some controls that logically belongs together and is reused many places in different windows. The controls are always placed inside a grid.
Instead of copying the controls (and the code behind) each time I want to use them, I would like to define and maintain them in a single xaml file as a single UserControl.
I have this now:
<Grid>
<Grid.ColumnDefinitions>
[ColumnDefinitions...]
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
[RowDefinitions...]
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock Text="Caption" />
<Border Padding="2" x:Name="myBorder">
<TextBox TabIndex="1" x:Name="myTxt"/>
</Border>
</StackPanel>
<ListBox x:Name="myList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,50,5,0" Height="100" VerticalAlignment="Top" Visibility="Collapsed" />
[More controls..]
</Grid>
But I want to reuse this part:
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock Text="Caption" />
<Border Padding="2" x:Name="myBorder">
<TextBox TabIndex="1" x:Name="myTxt"/>
</Border>
</StackPanel>
<ListBox x:Name="myList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,50,5,0" Height="100" VerticalAlignment="Top" Visibility="Collapsed" />
as a single control - but how do I define the Grid.Column when using the control (somehow supplying it as a parameter)? - and how do I set the Grid.RowSpan value (eventhough the code is moved to a new xaml file, and not defined inside a grid)?
Any comments?

Make them into a separate usercontrol, then include that in your project.
If you're using Blend, it's really easy, just select all the controls, right click and Make into Usercontrol.

You could also make it into a resource.
Define it in a ResourceDictionary and include the dictionary the places you want to use it.
There is one catch - the resource dictionary returns the same instance everytime - so you have to add the attribute x:Shared="false".
But the wpf way is to figure out how you can do it with a DataTemplate :)

Related

How to change listview datatemplate in c# dynamically

I have a custom made item template for my listview with which values getting bound in .xaml itself.
I want to change one of the image dynamically in c# side.
I know in c# I can change in ContainerContentChanging but I am not able to access the custom templates image in c# please guide me out of this problem.
Here is my listview
<ListView ItemsSource="{Binding testList}" IsItemClickEnabled="True" ContainerContentChanging="ListView_ContainerContentChanging">
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="2" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.80*"></ColumnDefinition>
<ColumnDefinition Width="0.20*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Margin="0,8,0,8" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="20" Margin="0,8,0,4" Text="{Binding textVal1}" />
<Image Name="imgStatus" Source="ms-appx:///Assets/Icons/ic_test.png" Height="36" />
</StackPanel>
<TextBlock FontSize="18" Margin="0,0,0,4" Text="{Binding textVal2}" />
<TextBlock FontSize="21" Margin="0,0,0,0" Text="{Binding textVal3}" TextWrapping="WrapWholeWords" MaxLines="2" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I thought lets have the data template in a separate c# file ... but I am not able to link up the c# file inside the grid.
Any help to access the image from the data template is appreciated.
If you define the templates as a static resource, or elsewhere where they can be referenced in XAML, you can use a custom converter with appropriate bindings to handle the change in template.
However, I believe changing the ListView.ItemTemplate will affect all items in the collection. In order to change the template of individual items, you will have to add a wrapper object and modify that object's content (which you can do in a similar manner).
I understand you wished to change the template in C#, but I believe XAML's declarative style should be sufficient.

WPF SettingsDialog using a ListView's SelectedItem and a ContentPresenter

I thought I cooked up something rather brilliant, but it's not working (yet!).
I am trying to get rid of all code behind in a SettingsDialog where a SettingsGroup can be selected by the user, which will consequently be visualized to the right of ListView (a similar settings dialog as Visual Studio has).
I was tipped to use a ContentPresenter, which I did, but then later realized that the tipper probably meant to rely on only XAML to get the job done.
This is what I got so far.
Declaring the SettingsGroups in XAML:
<Grid.Resources>
<w:DefinePathsUserControl x:Key="DefinePathSettingsGroup"></w:DefinePathsUserControl>
<w:HideShowTvShowsUserControl x:Key="HideShowTvShowsSettingsGroup"></w:HideShowTvShowsUserControl>
</Grid.Resources>
Add the left hand side SettingsGroup selector (a simple ListView):
<ListView
x:Name="SettingsGroupSelector"
Grid.Row="0"
Grid.Column="0">
<ListViewItem
x:Name="PathSetting"
Tag="{StaticResource DefinePathSettings}"
Content="Path"/>
<ListViewItem
x:Name="HideShowTvShowsSetting"
Tag="{StaticResource HideShowTvShowsSettings}"
Content="Hide/Show TV Shows"/>
</ListView>
And then I thought I could simply bind the ContentPresenter to the tag of the selected item in the ListView, like:
<ContentPresenter
x:Name="SettingsContentPanel"
Grid.Row="0"
Grid.Column="2"
Grid.ColumnSpan="2"
Content="{Binding Source=SettingsGroupSelector, Path=SelectedItem.Tag}" />
Unfortunately for me, this does not show anything in my ContentPresenter. I also don't get any errors in my output window.
Who can help me further?
Change
<ContentPresenter
x:Name="SettingsContentPanel"
Grid.Row="0"
Grid.Column="2"
Grid.ColumnSpan="2"
Content="{Binding Source=SettingsGroupSelector, Path=SelectedItem.Tag}" />
to this
<ContentPresenter
x:Name="SettingsContentPanel"
Grid.Row="0"
Grid.Column="2"
Grid.ColumnSpan="2"
Content="{Binding ElementName=SettingsGroupSelector, Path=SelectedItem.Tag}" />
More information on specifying the Binding Source

How do you add items to a Canvas that is part of a UserControl?

I have a usercontrol that contains 2 rows. 1st row has a label and 2nd row has a scrollviewer with a canvas:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="TITLE" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Name="label1" VerticalAlignment="Top" FontSize="26" />
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<Canvas Background="White" />
</ScrollViewer>
</Grid>
In my main window.xaml I'm trying to add this user control and then add items to it's canvas.
<local:UserCanvas>
<label Content="Test" />
</local:UserCancas>
There problem here is that when I had that label in there it just overrides the "TITLE" label in row 1 of the user control. How do I add things so that they are placed on the cavas of the UserControl?
Instead of defining the Content in your UserControl, define the UserControl.ContentTemplate
For example, instead of writing
<UserControl ...>
<Grid>
...
<Canvas />
...
</Grid>
</UserControl>
use
<UserControl ...>
<UserControl.ContentTemplate>
<DataTemplate>
<Grid>
...
<Canvas>
<ContentPresenter Content="{TemplateBinding Content}"/>
</Canvas>
...
</Grid>
</DataTemplate>
</UserControl.ContentTemplate>
</UserControl>
If you use the first syntax, then specifying the Content when you use your UserControl will result in the existing Content getting overwritten, so your rendered Visual Tree ends up looking like this:
<local:UserCanvas>
<label Content="Test" />
</local:UserCancas>
By using the 2nd syntax, you're wrapping the Content in your ContentTemplate, so the rendered Visual Tree ends up looking like this:
<local:UserCanvas>
<Grid>
...
<Canvas>
<label Content="Test" />
</Canvas>
...
</Grid>
</local:UserCanvas>
I would try adding a public property (that you check and respond to in the user control's Page_Load()) and/or public method (that you just call from outside the control) to the user control which you can then access from your main program. Which way to go depends a bit on how complicated the actions you're needing to take will be. It looks to me like this will be simple enough to handle through the public property + Page_Load() method.

Dividing WPF window

I want to divide my window (wpf) in three columns: left column must be DockPanel ( I think StackPanel will not work on Canvas), the right column should be another DockPanel holding a listbox and in the middle I need to have a Canvas.
This is what I have done and I am having problem with left column since it is not expandable. I need the left column as holder of custom object so that user could drag/drop them on canvas. Please advise.
<DockPanel LastChildFill="True" Background="LightGray" Margin="5">
<Expander Header="Controls" Background="Gray" Margin="2"
Content="{StaticResource FC}" DockPanel.Dock="Top"
IsExpanded="True" Width="200" />
</DockPanel>
<GridSplitter Focusable="False" Width="2" Background="LightGray"
VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
<lib:MyCanvas x:Name="myCanvas" FlowDirection="LeftToRight"
Background="White" AllowDrop="True"
Mouse.MouseMove="MyCanvas _MouseMove">
</lib:MyCanvas >
Also, what control should be used on the right side so that can hold a listbox?
Looking at various question you have asked, it looks like you are looking at building an application similar to Visual studio. I would suggest you to look at following great series of articles on CodeProject similar to your requirement i.e. having ToolBox, various ToolBox items , a designer, drag & drop items on designer etc. -
WPF Diagram Designer - Part 4, Part 3, Part 2, Part 1
You will just have to add a PropertyGrid on the right side and bind your selected ToolBox item in designer with it.
You can embed another grid, or canvas, or other kind of container inside of a grid, you don't have to. But you have a GridSplitter so by definition it needs to be placed inside of a Grid in order to provide its functionality, otherwise it doesn't do anything.
It sounds like you want to do something like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5" />
<ColumnDeifnition Width="*" />
<ColumnDefinition Width="100" /> <!-- whatever size you need here --->
</Grid.ColumnDefinitions>
<DockPanel LastChildFill="True" Background="LightGray" Margin="5" Grid.Column="0">
<Expander Header="Controls" Background="Gray" Margin="2"
Content="{StaticResource FC}" DockPanel.Dock="Top"
IsExpanded="True" Width="200" />
</DockPanel>
<GridSplitter Focusable="False" Width="2" Background="LightGray" Grid.Column="1"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ResizeDirection="Columns"/>
<lib:MyCanvas x:Name="myCanvas" FlowDirection="LeftToRight" Grid.Column="2"
Background="White" AllowDrop="True"
Mouse.MouseMove="MyCanvas _MouseMove">
</lib:MyCanvas >
<ListBox Grid.Column="3" ... />
</Grid>
This layout gives you your 3 columns, the 2nd column is your splitter that lets you resize the first 2 columns (sliding back and forth between them), and your 3rd column is a fixed size.
Create a Grid with 3 columns. For the 1st and 3rd put a DockPanel as child.
I think the general rule is don't put others under canvas, not the other way round.

C# .Net WPF Custom User Control zIndex issue with similar user control

User control details:
Have created drop down list control (Like as combo box), clicking on down arrow button, it displays a list below the text box
I have set zIndex property of my User control
Issue:
Case 1: When there is another user control (other than my custom user control), and if drop down list is displayed, other user control hides behind my user control. This is perfectly Ok
Case 2: There are 2 Custom User controls, if list is displayed from first user control, second user control appears on the list. This is where i am facing issue
XAML of my control is as below
<UserControlx:Class="UserControls.AutoCompleteComboBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Panel.ZIndex="1110" LostFocus="UserControl_LostFocus" Height="Auto">
<Canvas Name="MainCanvas">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="150"></ColumnDefinition>
<ColumnDefinition Width="20"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox Name="autoTextBox" Height="20" MinWidth="150" Width="Auto" MinHeight="20" Style="{DynamicResource AutoCompleteBox}" BorderThickness="2"
Margin="0,0,0,0" TextWrapping="NoWrap" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
<Button Content="6" FontFamily="Marlett" Grid.Row="0" Grid.Column="1" FontSize="15" Margin="0,0,0,0" Height="20" Width="20" HorizontalAlignment="Right" VerticalAlignment="Top" Background="{StaticResource BackgroudBlueBrush}" Click="Button_Click" Padding="0" Cursor="Hand"></Button>
<StackPanel Grid.Row="1" Grid.ColumnSpan="2" >
<ListBox Name="suggestionListBox" SelectionChanged="suggestionListBox_SelectionChanged" MouseDown="suggestionListBox_MouseDown"
Background="LightYellow" SnapsToDevicePixels="True"
Visibility="Collapsed"
MinWidth="150" IsHitTestVisible="True" MinHeight="70" Height="70"
VerticalAlignment="Top" LostFocus="suggestionListBox_LostFocus"/>
</StackPanel>
</Grid>
</Canvas>
</UserControl>
Your approach is not the right one to correctly manage the overlap of controls. Perhaps you may create some trick using the ZIndex property, but that won't be the solution.
If you need a drop-down control, the best way is to use a Popup control and play around it. Basically, it creates another borderless window, being child of the yours.
Another approach, maybe simpler but not good as the Popup, is using an Adorner. Maybe this one is the most similar techinique to the yours.
Cheers
Have you tried setting the ZIndex of the StackPanel to 1+ the control's zindex? That should raise the drop down portion above any other instance of your user control.
Canvas.ZIndex can be used on StackPanels.

Categories