Can you repeat a block of controls inside a Ribbon? - wpf

My app has a number of ribbon tabs and there is a certain group of ribbon controls that needs to be repeated on a few of these tabs. These are not dynamic content, just static elements that are used repeatedly.
I could just repeat the XAML for those controls in 2-3 places but it seemed that there should be a cleaner way to do this...
I have tried to create a UserControl to house the repeated elements, but with mixed success. I've pasted below what I've done. This does work in the sense that the contents of the UserControl are shown within each RibbonTab; but here are the problems:
Without the UniformGrid (or any other 'standard' panel like WrapPanel ) it is not possible to include multiple Ribbon controls in the UserControl. But because this panel controls the layout these controls don't correctly participate in the usual Ribbon layout rules (such as when your window is resized & the ribbon control sizes can change.)
I have to wrap the UserControl inside a RibbonGroup in each place it is used. Initially I intended to have the RibbonGroup be the main panel inside the UserControl, but this did not layout correctly - all the subsidiary controls were rendered almost entirely below the lower ribbon border?
I have the sense that some type of templating solution may be a better choice. I have read however that some of the Ribbon controls were designed not following typical WPF standards for how templates are used and that adds a lot of uncertainty.
Note that while I'd probably prefer a XAML-only approach, if some code behind neatly gets this done I think that would be fine.
UserControl:
<UserControl x:Class="ribbon1.SampleUC" ...>
<UniformGrid Columns="2" Rows="1">
<RibbonButton
Label="Zoom In"
SmallImageSource="..."
/>
<RibbonButton
Label="Zoom Out"
SmallImageSource="..."
/>
</UniformGrid>
</UserControl>
Main ribbon:
<Ribbon>
...
<RibbonTab Header="Tab1">
...
<RibbonGroup>
<l:SampleUC/>
</RibbonGroup>
</RibbonTab>
<RibbonTab Header="Tab2">
...
<RibbonGroup>
<l:SampleUC/>
</RibbonGroup>
</RibbonTab>
...
</Ribbon>

Replace the UserControl Tag in SampleUC.xaml with RibbonGroup and change the parent class in the code behind file.
<RibbonGroup x:Class=".SampleUC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UniformGrid Columns="2" Rows="1">
<RibbonButton Label="Zoom In" />
<RibbonButton Label="Zoom Out" />
</UniformGrid>
public partial class SampleUC : RibbonGroup
{
public SampleUC()
{
InitializeComponent();
}
}
Now you can use it like that
<Ribbon>
<RibbonTab Header="Tab1">
<l:SampleUC/>
</RibbonTab>
</Ribbon>

Related

WPF Popup placement relative to main window

I have a WPF application that uses one window as main window. Now I want to display various popups centered in the window. This works fine when the popup is placed inside the window. However, I have implemented the popups as user control; meaning that the main window contains a user controls which itself contains the actual popup. So the UI element tree looks like this:
Window --> UserControlPopup --> Popup
The popup inside the user control is declared like this:
<UserControl x:Class="X.Custom_Controls.ErrorPopup"
...
xmlns:local="clr-namespace:X.Custom_Controls"
mc:Ignorable="d" d:DesignHeight="360" d:DesignWidth="500">
<Popup Name="errorPopup" Height="360" Width="500" Placement="Center" StaysOpen="True" PlacementTarget="{Binding ElementName=mainWindow}">
...
</Popup> </UserControl>
The mainWindow element name is the one of my main window, which is declared as follows:
<Window x:Class="X.MainWindow" x:Name="mainWindow" ...>
The problem is that the popup is not placed in the center, but on the left side. So my guess is that the popup cannot resolve the elementName properly (since it's a child of a child of the mainWindow). Does anyone know how I can solve this issue in XAML?
Update: the solution was to use
PlacementTarget="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
Try to acces your main window through x:Reference. This one should work:
<MainWindow x:Name="mainWindow">
<UserControl Tag="{x:Reference mainWindow}">
</MainWindow>
<UserControl x:Name="userControl">
<PopUp PlacementTarget="{Binding Path=Tag, ElementName=userControl}"/>
</UserControl>
Such a way you can use your UserControl in another Controls/UserControls and you will not need to modify it, just set the Tag property from outside, if you use {RelativeSource AncestorType=Window} you will need to modify it.
The solution was to use the following as PlacementTarget:
PlacementTarget="{Binding RelativeSource={RelativeSource AncestorType=Window}}"

Why do my commandbindings only work if button is in toolbar?

I have a window with a toolbar which contains some buttons with commands.
The Buttons stopped working since i replaced the toolbar with a stackpanel containing the buttons.
In my understanding this shound not make any difference. The buttons still have the Command property set, i did not change anything in my custom commands class and the CommandBinding are also still the same. They are implemented some grids and usercontrols deeper than the button, but they do work, as long as the buttons are in a ToolBar control!
If i implement CommandBindings directly in the Window they work (but that is not what i want)
Here's the code (abridged):
<Window>
<Grid>
<StackPanel>
<Button Command="gui:GuiCommands.Hello">Hello</Button>
</StackPanel>
<Grid>
<TabControl>
<TabItem Header="MyTab">
<Grid>
<Grid.CommandBindings>
<CommandBinding Command="gui:GuiCommands.Hello" Executed="hello_Clicked"/> <!-- THIS WOULD NOT WORK -->
</Grid.CommandBindings>
<Grid>
</TabItem>
</TabControl>
</Grid>
</Grid>
<Window.CommandBindings>
<CommandBinding Command="gui:GuiCommands.Hello" Executed="hello_Clicked"/> <!-- THIS WOULD WORK -->
</Window.CommandBindings>
</Window>
I know it would not compile but i had to simplify it. This works as soon as i replace "StackPanel" with "ToolBar" with my app. How can that be?
Okay, i guess is figured it out by my self again (why does this always happen right after i posted the question?)
Short: I needed to set FocusManager.IsFocusScope="true" on the StackPanel
Long: see the answer to this question: Do I have to use CommandTarget? I thought any focused element would receive the Command
A StackPanel only arranges child elements into a single line that can be oriented horizontally or vertically.
While a Toolbar provides a container for a group of commands or controls.
So what happens if you put a StackPanel element inside of the ToolBar
<ToolBar>
<StackPanel>
<Button Command="gui:GuiCommands.Hello">Hello</Button>
</StackPanel>
</ToolBar>

How to make controls resize in WPF equivalently to Windows Form's anchor/dock properties?

I've read so many solutions to this problem. Every one of them fails to solve my problem. No matter what container I put the control into or what properties I set on the control/container it will not budge.
I have a scroll viewer with a control within. I want it to resize with the window when the user resizes it at runtime. All I need is anchor=top, bottom, left, right. I don't understand why this is so elusive in WPF and why container objects and all kinds of property assignments need to be involved to accomplish what a single property can in Windows Forms. But every solution to this problem still results in my control staying at exactly its design time size as the window is resized at runtime. What's the simple way to get a grip on dynamic control sizing in WPF?
This has caused me grief as well and AlexK helped me see the light. The trick is NOT to set the Height and Width.... Set these to AUTO and use the MARGIN to set the size. Set the HORIZONTALALIGNMENT and VERTICALALIGNMENT to STRETCH and then the anchor functionality works.
The control needs to stretch, that's all there should be to it:
<MyControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
Stretch replaces setting anchors to both respective sides.
For help on panels see this overview. Also see the documentation of the layout system.
Most controls automatically stretch, if you have a DataGrid it should stretch too, this example contains a DataGrid and a TextBlock which shows its size:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DataGrid Name="grid">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="Name"/>
<DataGridTextColumn Binding="{Binding Tag}" Header="Occupation"/>
</DataGrid.Columns>
<FrameworkElement Name="Skeet" Tag="Programmer"/>
<FrameworkElement Name="Gravell" Tag="Programmer"/>
<FrameworkElement Name="Steve" Tag="Coffee Getter"/>
</DataGrid>
<TextBlock Grid.Row="1">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}, {1}">
<Binding ElementName="grid" Path="ActualWidth"/>
<Binding ElementName="grid" Path="ActualHeight"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</Window>
If you size the window down the DataGrid's ScrollBars should appear.
I assume the control that does not resize is your custom control.
Use DockPanel as a container.
Remove explicit Width and Height properties from your control.
If you work in VS2008, then this causes inconvenience, because you control would collapse to the minimal size when viewed in the designer.
Expressions Blend and starting from VS2010 both respect designer namespace, so you can specify design time only control size.
For that add the following to your control:
<UserControl ...
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignWidth="WWW" d:DesignHeight="HHH">
....
</UserControl>
d:DesignWidth and d:DesignHeight specify the design time width and height.
This question is old but, since I found my here I thought I would throw out my solution. The code below is for a tab control with two tabs and each tab contains a control to fill the tab. I removed the explicitly set width and height, and set the horizontal and vertical alignments to auto on all the controls I wanted to resize. The tab control stretches wit the main window. The controls in the tabs stretch to fill the tabs. The information came from the answers here. I just put up a complete example.
Hope this is useful to someone.
<TabControl HorizontalAlignment="Stretch"
Margin="91,0,0,0" Name="tabControl1"
VerticalAlignment="Stretch" >
<TabItem Header="DataGrid" Name="tabItem1">
<Grid>
<DataGrid AutoGenerateColumns="False"
HorizontalAlignment="Stretch"
Name="dgFTPLog"
VerticalAlignment="Stretch"
Margin="6,6,0,0" />
</Grid>
</TabItem>
<TabItem Header="Log" Name="tabItem2">
<Grid>
<TextBox
HorizontalAlignment="Stretch"
Margin="6,6,0,0" Name="txtLog"
VerticalAlignment="Stretch"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
TextChanged="txtLog_TextChanged" />
</Grid>
</TabItem>
</TabControl>
I've came across this issues as well.
and Attempted at binding to parent controls ActualHeight and ActualWidth properties except this only works if you do it via code behind not by XAML.
i.e
XAML
<MyParentControl x:name="parentControl" SizeChanged="parentControl_SizeChanged">
<ChildControl x:name=childControl" />
</MyParentControl>
in the .cs code behind
private void parentControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
childControl.Height = parentControl.ActualHeight;
childControl.Width = parentControl.ActualWidth;
}

Silverlight - specify stackpanel contents of a user control in the parent

I created a user control called 'RibbonTabX' which contains a stackpanel named 'spMain'. What I'd like to do, is when I declare an instance of my 'RibbonTabX' in xaml, within that same xaml I'd like to specify controls which will be inside the child stackPanel 'spMain'. Here is the code which will make what I'm trying to do much clearer:
<ribbon:RibbonTabX strHeaderText="Testing 123...">
<ribbon:RibbonTabX.spMain>
<sdk:Label Content="Hello" />
<sdk:Label Content="World" />
</ribbon:RibbonTabX.spMain>
</ribbon:RibbonTabX>
In the parent of RibbonTabX, I want to specify child contents of the stackpanel within my user control 'RibbonTabX'. Just like you can do with a 'TabItem' control. Any ideas how I can do this?
Thanks!
You need to create a custom content control, not a user control.
Start with this article
It is more complex than a user control as you have to hand-craft a generic template for it, but they are more versatile.
You want to use a ContentControl. Rather than specify that those controls go in the stack panel you probably should just place the Content in the stack panel. Have your RibbonTabX derive from ContentControl rather than UserControl, then where it is appropriate put the <ContentPresenter /> then the user of the ribbon can put whatever into it.
<ribbon:RibbonTabX strHeaderText="Testing 123...">
<StackPanel>
<sdk:Label Content="Hello" />
<sdk:Label Content="World" />
</StackPanel>
</ribbon:RibbonTabX>
Here is the most basic ContentControl possible:
<ContentControl x:Class="SilverlightControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="Orange">
<ContentPresenter />
</Grid>
</ContentControl>

inherit WPF window, why i can not see the Parent's buttons in the child's window

i have a base WPF window and a second WPF window which derived from it.
if i add new items to the child's window, i can not see the buttons that derived from the base window anymore.
anyone? idea???
thanx! tali
There is no visual inheritance in WPF. You should use a UserControl instead.
Edit:
<UserControl x:Class="MyUserControl">
<StackPanel>
<Button Content="Push me" Click="MyClickHandlerInUserControl" />
</StackPanle>
</UserControl>
You can use this UserControl in any View e.g:
<Window x:Class="MyWindow"
xmlns:uc="clr-namespace:MyNamespace.MyUserControls" >
<StackPanel>
<uc:MyUserControl /> <!--You can use any properties you have declared in your usercontrol -->
<Button Content="Some other Button" Click="MyClickHandlerInWindow" />
</StackPanel>
</Window>

Resources