Bind visibility of a LayoutAnchorable to a checkable MenuItem - c#

C#, WPF. I would like to show/hide AvalonDock panels using checkable menu items. Although I could do this using _Click events I believe it would be good practice to use binding instead and that it should be possible to achieve this entirely using XAML.
I suspect that the answer should be along the lines of this one and have based my attempt on one of the answers given there.
This code compiles and runs, but there is no link between the menu being checked and the anchorable pane being visible. The key line is this:
<MenuItem Header="Panel" Name="PanelVisible" IsCheckable="True" IsChecked="{Binding Path=testAnchorable.IsVisible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
How can I get this working?
<Window x:Class="TestAvalon.MainWindow"
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"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:dock="http://schemas.xceed.com/wpf/xaml/avalondock"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Menu Height="18" HorizontalAlignment="Stretch" Name="menu1" VerticalAlignment="Top" Grid.Row="0">
<MenuItem Header="View">
<MenuItem Header="Panel" Name="PanelVisible" IsCheckable="True" IsChecked="{Binding Path=testAnchorable.IsVisible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</MenuItem>
</MenuItem>
</Menu>
<dock:DockingManager x:Name = "Dockman" DataContext = "{Binding DockManagerViewModel}"
DocumentsSource = "{Binding Documents}" DockPanel.Dock = "Left" Grid.Row = "1" >
<dock:LayoutRoot x:Name = "_layoutRoot" >
<dock:LayoutPanel x:Name = "_layoutPanel" >
<dock:LayoutAnchorablePane DockWidth="400">
<dock:LayoutAnchorable x:Name ="testAnchorable" Title = "TEST PANE" IsSelected = "True">
<TextBlock Name="tb" Text="*****"/>
</dock:LayoutAnchorable >
</dock:LayoutAnchorablePane >
</dock:LayoutPanel >
</dock:LayoutRoot >
</dock:DockingManager>
</Grid>
</Grid>

This question now has a solution here:
Binding an AvalonDock LayoutAnchorable IsVisible property
(There are two answers given there and I think they both do the job.)

Related

How can I keep a menu submenu item open?

I'm trying to create a somewhat complex menu item that would allow a user to create a new class. The problem into which I am running is that when I click on a numeric up-down ( from the xceed toolkit ) that the menu item closes, even with the property StaysOpenOnClick set to true.
Users will not like that.
To reproduce, create a WPF project and add the Extended WPF Toolkit through NuGet, then drop the following code into your mainwindow class :
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WhyDoesMyMenuItemCloseWhenClicked"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
x:Class="WhyDoesMyMenuItemCloseWhenClicked.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="21"/>
<RowDefinition />
</Grid.RowDefinitions>
<Menu FontWeight="Bold">
<MenuItem Header="_File">
<MenuItem StaysOpenOnClick="True">
<Grid Height="50" Width="50">
<xctk:IntegerUpDown/>
</Grid>
</MenuItem>
</MenuItem>
</Menu>
</Grid>
</Window>
When I click the text field of the integer up-down, the menu closes.
Why does that keep happening? How can I make it NOT happen?
I have figured out a solution. It is sort of a terribly hacky workaround, but it does the job quite well :
The change is that you create a MenuItem within the MenuItem. Then you define your control within the sub MenuItem's MenuItem.Header property, and set that MenuItem's StaysOpenOnClick property to true.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WhyDoesMyMenuItemCloseWhenClicked"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
x:Class="WhyDoesMyMenuItemCloseWhenClicked.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="21"/>
<RowDefinition />
</Grid.RowDefinitions>
<Menu FontWeight="Bold">
<MenuItem Header="_File" StaysOpenOnClick="True">
<MenuItem Header="_StaysOpenOnClick">
<MenuItem StaysOpenOnClick="True">
<MenuItem.Header>
<xctk:IntegerUpDown/>
</MenuItem.Header>
</MenuItem>
</MenuItem>
</MenuItem>
</Menu>
</Grid>
</Window>
you can make use of StaysOpenOnClick Property to achieve this

MinWidth, MaxWidth, MinHeight, MaxHeight don't have effect for <UserControl>

I'm learning to create WPF application with Caliburn Micro framework.
Following the tutorial on the homepage: Basic Configuration, I removed the default generated <Window> xaml, instead, I have <UserControl> xaml and bootstrap it via my AppBoostrapper.
Here is the MainView.xaml:
<UserControl x:Class="SmartRenamer.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
MinHeight="300"
MinWidth="300"
MaxHeight="500"
MaxWidth="1000">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Menu IsMainMenu="True" Grid.Row="0">
<MenuItem Header="_File">
<MenuItem Header="_Open Folder..." x:Name="OpenFolder"></MenuItem>
</MenuItem>
<MenuItem Header="_About" />
</Menu>
<ScrollViewer HorizontalScrollBarVisibility="Visible" Grid.Row="1" MaxHeight="500" MaxWidth="1000">
<StackPanel>
<DataGrid x:Name="FilesList">
</DataGrid>
</StackPanel>
</ScrollViewer>
</Grid>
</UserControl>
The problem is I want to set the MinWidth, MaxWidth, MinHeight, MaxHeight for my application, but it seems that those configuration in the MainWindow.xaml isn't working.
Here is the two screenshots, where the window is still can be re-sized out of the defined range:
What am I wrong here?
The width and height properties of an UserControl is set by the container that holds that control. I.e. the DesignWidth and DesignHeigth that are used in the design-time are set in the UserControl but in runtime it uses the values set by the container.
I am not experienced in Caliburn Micro but it seems like you can define the design properties in the bootstrapper:
var settings = new Dictionary<string, object>
{
{ "Height" , 300},
{ "Width" , 300},
};
DisplayRootViewFor<ViewModel>(settings);

New item activated in Conductor does not show it

I'm activating new item in conductor using Caliburn.Micro.Contrib's ConductResult. Conductor is of type Conductor<IScreen>.Collection.OneActive, and there is already one item showing and working correctly.
The new item however is not shown after it was activated. I already checked and the conductor's ActiveItem is set to that new item, new item is activated as well. View's IsVisible of new item is also set to true, so I don't understand why it is not visible.
XAML of the conductor's view is pretty simple:
<UserControl x:Class="..."
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ActiveItem, Converter={StaticResource objectTypeConverter}}" Margin="5" />
<ItemsControl Grid.Row="1" ItemsSource="{Binding Items}" BorderBrush="Aqua" BorderThickness="10 ">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Converter={StaticResource objectTypeConverter},ConverterParameter=something}" Margin="5" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ContentControl Grid.Row="2" x:Name="ActiveItem" />
</Grid>
</UserControl>
(TextBlock and ItemsControl are there for debugging purposes; they prove that new item is conducted within conductor (i.e. Items collection contains it) and new item is set as ActiveItem)
In my case, I was using IoC.Get<IShell> to access the parent viewmodel.
The default bootstrapper says container.PerRequest<IShell, ShellViewModel>();
That’s why I was getting another instance of my ShellViewModel class, activated the item just fine, but no UI was updating.
One way to fix is replace container.PerRequest with container.Singleton.
Another is change IoC.Get<IShell>() into ( (IShell)Parent )

How to stretch/maximise an AvalonDock DocumentPane

I am using AvalonDock v 1.3 on .NET 3.5.
I have added two Document Panes at design time to a DockingManager. The first one is set to be visible and the second is hidden/collapsed (see Visibility="Collapsed" below).
When I launch the application, the second Document Pane is not visible, which is the intended behaviour but unfortunately, the visible document panel does not display stretched to the edges of the main window despite the fact that HorizontalAlignment is set to "Stretched". How would I go about making this clip(or maximise) to the edges of the allowed area?
This is the xaml that I am using:
<ad:DockingManager x:Name="dockManager" Grid.Row="1">
<ad:ResizingPanel Name="resizePanel" Orientation="Horizontal">
<ad:DocumentPane Name="visibleDocumentPane" HorizontalAlignment="Stretch" >
<ad:DocumentContent Title="A"/>
<ad:DocumentContent Title="B"/>
</ad:DocumentPane>
<ad:DocumentPane Name="collapsedDocumentPane" Visibility="Collapsed">
<ad:DocumentContent Title="A"/>
<ad:DocumentContent Title="B"/>
</ad:DocumentPane>
</ad:ResizingPanel>
</ad:DockingManager>
Thanks,
Dave
As per request, here is the full XAML:
<Window x:Class="AvalonDockSampleProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ad="clr-namespace:AvalonDock;assembly=AvalonDock"
Title="MainWindow" Height="421" Width="948">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="24"/>
<RowDefinition Height="*"/>
<RowDefinition Height="24"/>
</Grid.RowDefinitions>
<Menu>
<MenuItem Header="File">
<MenuItem Header="Create DockableContent" Click="CreateDockableContent"/>
<MenuItem Header="Layout">
<MenuItem Header="Save" Click="SaveLayout"/>
<MenuItem Header="Restore" Click="RestoreLayout"/>
</MenuItem>
<MenuItem Header="Exit"/>
</MenuItem>
</Menu>
<ad:DockingManager x:Name="dockManager" Grid.Row="1">
<ad:ResizingPanel Name="resizePanel" Orientation="Horizontal">
<ad:DocumentPane Name="visibleDocumentPane" HorizontalAlignment="Stretch" >
<ad:DocumentContent Title="A!"/>
<ad:DocumentContent Title="B!"/>
</ad:DocumentPane>
<ad:DocumentPane Name="collapsedDocumentPane" Visibility="Collapsed">
<ad:DocumentContent Title="A"/>
<ad:DocumentContent Title="B"/>
</ad:DocumentPane>
</ad:ResizingPanel>
</ad:DockingManager>
<StatusBar Grid.Row="2">
<StatusBarItem Content="AvalonDock 1.3 Sample Project"/>
</StatusBar>
</Grid>
</Window>
You are right, it seems problematic to define your collapsedDocumentPane in XAML, as AvalonDoc will reserve the space for it (it's tab header or whatever) completely disregarding Visibility="Collapsed", so I ended up adding/removinhg mine in code. So:
First, remove your "collapsedDocumentPane" from XAML (I am commenting it out, so that it can be used as a reference from code behind later):
<ad:DockingManager x:Name="dockManager" Grid.Row="1">
<ad:ResizingPanel Name="resizePanel" Orientation="Horizontal">
<ad:DocumentPane Name="visibleDocumentPane" HorizontalAlignment="Stretch" >
<ad:DocumentContent Title="A"/>
<ad:DocumentContent Title="B"/>
</ad:DocumentPane>
<!--<ad:DocumentPane Name="collapsedDocumentPane" Visibility="Collapsed">
<ad:DocumentContent Title="A"/>
<ad:DocumentContent Title="B"/>
</ad:DocumentPane>-->
</ad:ResizingPanel>
</ad:DockingManager>
And recreated that xaml in code behind (in you xaml.cs file):
private DockablePane _collapsedDocumentPane;
private DockablePane CollapsedDocumentPane
{
get
{
if (_collapsedDocumentPane== null)
{
_collapsedDocumentPane= new DockablePane();
var a = new DockableContent
{
Title = "A",
DataContext = _youViewModel, //if you pass data context
DockableStyle = DockableStyle.AutoHide,
Content = new RadGridView(), //just a sample control
};
var b = new DockableContent { Title = "B"};
_collapsedDocumentPane.Items.Add(a);
_collapsedDocumentPane.Items.Add(b);
}
return _errorsDockablePane;
}
}
Then a method that adds or removes it:
private void EvaluateCollapsedDocPaneVisibility()
{
//don't know your scenario
if (NeedToDisplay_CollapsedDocPane)
{
if (!resizePanel.Children.Contains(CollapsedDocumentPane))
resizePanel.Children.Add(CollapsedDocumentPane);
}
else
{
if (resizePanel.Children.Contains(CollapsedDocumentPane))
resizePanel.Children.Remove(CollapsedDocumentPane);
}
}
Note, the property is lazy loaded - constructed only when needed.
So now all you gotta do is call the method above whenever you need to add or remove.
This is just a sample, you can add an arg to the method to tell it what to do, or whatever, hope this helps/gets you going.

How to make a control dock in a wpf

I'm trying to make my menu bar fill the screen horizontally like a menu bar should look. When I run the program the window is set to maximize already but the menu bar only fills half the screen on top. I'm not sure how to fix this. So just to be clear I'm trying to make my controls fit to appearance based on the size of the window.
Heres the code:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Royale" WindowStyle="ThreeDBorderWindow" SizeToContent="Manual" WindowState="Maximized" xmlns:my1="clr-namespace:System;assembly=mscorlib">
<Window.Resources>
<my1:Double x:Key="Width1">500</my1:Double>
</Window.Resources>
<Grid ShowGridLines="False" Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Menu Height="23" HorizontalAlignment="Left" Name="menu1" VerticalAlignment="Top" Width="503" IsMainMenu="True" Grid.ColumnSpan="2">
<MenuItem Header="File" HorizontalContentAlignment="Stretch">
<MenuItem Header="New">
<MenuItem Header="New Camper" />
</MenuItem>
</MenuItem>
<MenuItem Header="Edit" />
<MenuItem Header="View" />
<MenuItem Header="Add" />
</Menu>
</Grid>
</Window>
Using DockPanel:
http://www.wpftutorial.net/DockPanel.html
<DockPanel LastChildFill="True">
<Button Content="Dock=Top" DockPanel.Dock="Top"/>
<Button Content="Dock=Bottom" DockPanel.Dock="Bottom"/>
<Button Content="Dock=Left"/>
<Button Content="Dock=Right" DockPanel.Dock="Right"/>
<Button Content="LastChildFill=True"/>
</DockPanel>
The simple answer - set your menu to use Grid.ColumnSpan="3", since you have 3 columns.
However, I would recommend nesting your "main" grid inside of a DockPanel or a second Grid (with 2 rows). This way, as you add content (ie: add new columns to your grid), you won't have to constantly adjust your menu to compensate.

Categories