View is not being resolved with Toolbar ItemsSource - c#

It doesn't seem like the Caliburn Micro framework is retrieving my SinglePaintToolbarView when it is binded as a list of buttons in the toolbar of the ShellView. I would like the buttons to just display their text content when they are added to the toolbar. But, instead I'm getting this:
There doesn't appear to be any clickable buttons in the toolbar. I know my plugins are being loaded successfully, because I was able to bind one of the plugins in the list as a ContentControl and the view appeared. It just doesn't seem to work when I try to bind a list of the plugins in a toolbar.
Here is what I have:
ShellView.xaml
<UserControl x:Class="Starbolt.Views.ShellView"
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">
<Grid>
<ToolBarTray>
<ToolBar ItemsSource="{Binding Path=ToolbarPlugins}"/>
</ToolBarTray>
</Grid>
</UserControl>
ShellViewModel.cs
[Export(typeof(IShell))]
public class ShellViewModel : PropertyChangedBase, IShell
{
[ImportMany(typeof(IToolbarPlugin))]
private IEnumerable<IToolbarPlugin> _toolbarPlugins = null;
public IEnumerable<IToolbarPlugin> ToolbarPlugins { get { return _toolbarPlugins; } }
}
SinglePaintToolbarView.xaml
<UserControl x:Class="Starbolt.Plugin.SinglePaintTool.Views.SinglePaintToolView"
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="128" d:DesignWidth="32">
<Button Name="btnSinglePaintTool" Content="Single Paint Tool" Width="128" Height="32"/>
</UserControl>
SinglePaintToolViewModel.cs
[Export(typeof(IToolbarPlugin))]
public class SinglePaintToolViewModel : IToolbarPlugin
{
}

Basically, your design seems to be working. If you replace
<ToolBarTray>
<ToolBar x:Name="ToolbarPlugins"/>
</ToolBarTray>
(note that you do not need to bind the ItemsSource explicitly, you can just as well use the Caliburn Micro property name conventions) with the following:
<ListBox x:Name="ToolbarPlugins"/>
the SinglePaintToolView button is displayed as intended.
I suspect that the problem is with the ToolBar ControlTemplate, which most certainly restricts the toolbar items layout more than what for example a ListBox ControlTemplate does.
So my guess is that if you really want to use the ToolBar control to display your IToolbarPlugin views, you will probably have to design a dedicated ToolBar control template in your project.
Alternatively, you could implement a toolbar replacement using e.g. ListBox. This could be a start:
<ListBox x:Name="ToolbarPlugins">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>

Related

changing text in parent UserControl from Child UserControl WPF

If a ParentUserControl contains a TextBlock. The ParentUserControlalso contains ChildUserControl that has a TextBox. I want to set the TextBlock of ParentUserControl value from ChildTextBox. how can i ?
In other words somehow accessing the ParentUserControl and it's TextBlock element and then modifying it's value from ChildUserControl !
Update
i have a xaml window that contains a ParentUserControl that has a TextBlock. Now i am loading or adding another ChildUserControl into it on runtime. This newly added ChildUserControl contains a ChildTextBox. Now i want that when i input some value into this ChildTexBox the ParentUserControl's TextBlock should get that value and update itself.
Assuming we are not following any MVVM and a simple approach for this problem is,
Create a ChildUserControl with a textbox inside it as below,
<UserControl x:Class="SO52840402.ChildUserControl"
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:local="clr-namespace:SO52607887"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBox x:Name="ChildTextBox" />
</Grid> </UserControl>
Create a ParentUserControl which contains a TextBlock and ChildUserControl instance as shown below,
<UserControl x:Class="SO52840402.ParentUserControl"
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:local="clr-namespace:SO52607887"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock x:Name="ParentTextBlock" Text="Hallo World!"/>
<local:ChildUserControl x:Name="ChildUserControl" Grid.Row="1" />
</Grid> </UserControl>
Now create a TextChanged event for the TextBox which is under ChildUserControl from code behind of ParentUserControl constructor after "InitializeComponent" as shown below,
public ParentUserControl()
{
InitializeComponent();
ChildUserControl.ChildTextBox.TextChanged += OnChildTextBox_TextChanged;
}
private void OnChildTextBox_TextChanged(object sender, EventArgs e)
{
ParentTextBlock.Text = (sender as TextBox).Text;
}
Note:- This is a not a recommended approach. For best approach, follow MVVM pattern and understand your requirements and do the design. Since you need something from a child user control from a parent user control, a best approach is to have a ViewModel bind to parent and child and access child view model in parent viewmodel and do "what ever you want".

ReactiveUI - Views not rendering properly (or in full)

I'm in the process of starting a new project, using ReactiveUI and MahApps.Metro. The first hurdle I've hit is a problem of views not showing in their entirety. I have a Window, and in that Window I have a ViewModelViewHost from the RxUI library. It is a simple container for a nested view-model. The ActiveItem binding is properly binded to the view-model of the User Control, and the Button from the user control is visible on screen.
What I expect to see is a Window with a dark gray background and a button in the middle. What I see is the Window with its default background and a Button in the middle. This is not right. If I remove the button, I see nothing on the Window, only the default background.
It seems the ViewModelViewHost is only showing the actual contents of a UserControl and is disregarding what isn't considered a real Control, such as grids etc.
Has anyone come across this behaviour before?
<mah:MetroWindow x:Class="...MainWindow"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:rx="clr-namespace:ReactiveUI;assembly=ReactiveUI"
WindowStartupLocation="CenterScreen"
ShowTitleBar="False"
ShowCloseButton="False"
ShowMaxRestoreButton="False"
ShowMinButton="False"
Height="768"
Width="1024">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<rx:ViewModelViewHost ViewModel="{Binding ActiveItem}" />
</Grid>
</mah:MetroWindow>
<UserControl x:Class="...NestedView"
Name="TheUserControl"
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"
TextOptions.TextHintingMode="Auto"
TextOptions.TextRenderingMode="Auto"
d:DesignHeight="768" d:DesignWidth="1024">
<Grid Background="DarkGray">
<Button Width="200" Height="100" />
</Grid>
</UserControl>
This won't be a ReactiveUI problem, just a simple WPF one.
The ViewModelViewHost is centered in your window. While your UserControl has a DesignHeight and DesignWidth set, when it's rendered at runtime it will automatically size itself to the height and width of its content - the Button.
Add VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" to your ViewModelViewHost declaration and remove the other two Alignment attributes (they default to Stretch) and it should stretch its contents to the size of the Window while centering any item that has a fixed size.

Put a View Control in its ViewModel

I'm developing a Windows 8.1 Store app with C# and .Net Framework 4.5.1.
I'm trying to bind Password.SecurePassword to a ViewModel, and reading this SO answer I found a way to do it: Put the PasswordBox in my ViewModel.
But I don't know how to do it. I know how to bind Dependency Properties, but I don't know how to put that control on my ViewModel. This is my XAML:
<Page
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"
DataContext="{Binding MainViewModel, Source={StaticResource Locator}}"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<PasswordBox x:Name="userPassword" />
</Grid>
</Page>
What do I have to do?
You have several options but I'll just give you the basic option without third party libraries.
In your Page constructor. You can do something like this.
public Page()
{
var mainViewModel = this.DataContext as MainViewModel;
if(mainViewModel != null)
{
mainViewModel.PasswordBox = userPassword;
}
}
You can also set it on the Loaded event of the View and set the PasswordBox to the ViewModel.

Adding a UserControl to a TabItem

Originally I had my MainWindow(.xaml) that had a stackpanel and a frame. Within the stackpanel were three navigation buttons and the frame had one of the three Pages (based on which navigation button the user clicked). However, it seems that since I'm not doing a web app, that using Frame (and Pages?) is not the right way to go about it. So I changed the stackpanel and frame to a single tabcontrol (with tabs being what were the three buttons before). I also changed the Pages to usercontrols.
However, I'm having trouble finding a way to put the Pages (now UserControls) into the content of the tabitem, without using a Frame. I'm trying to do all of this within the MainWindow xaml.
my MainWindow.xaml:
<Window x:Class="ConstructedLanguageOrganizerTool.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Height="454" Width="573">
<Grid>
<TabControl HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Name="tabControl1">
<TabItem Header="Basics" Name="basicsTab">
//What can I use here instead of Frame?
</TabItem>
<TabItem Header="Words" Name="wordsTab">
<Grid>
<Frame Source="WordsPage.xaml"/>
</Grid>
</TabItem>
...
</TabControl>
</Grid>
</Window>
Am I going about this the wrong way? I think that I'm suppose to use some sort of databinding, maybe? Although, the more I look at things on data binging, the more I just get confused on that as well.
edit: here is my BasicsPage.xaml
<UserControl x:Class="ConstructedLanguageOrganizerTool.BasicsPage"
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" x:Name="basicsPage" Height="349" Width="334">
<Grid>
// Grid Row and Column defs here
//Number of textboxs and textblocks here.
</Grid>
</UserControl>
You just need to create an instance of UserControl and put it inside TabItem.
Say BasicsPage is your UserControl you want to put inside TabItem. All you have to do this:
<TabItem Header="Basics" Name="basicsTab">
<local:BasicsPage/>
</TabItem>
Define local namespace at root window where BasicsPage is defined in something like:
<Window x:Class="ConstructedLanguageOrganizerTool.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ConstructedLanguageOrganizerTool"> <-- HERE

Change between pages in WPF

I want to make a layout like the one used in any website - the header, sidebar and footer stay the same but the center part. I have multiple pages/windows to show in a wpf blend C# application and they are totally different. For example, stackoverflow has a layout for the homepage and another one for each Question. Here's another exemple:
I had to do that in a previous project and I used a single grid layout and then, for each page, I had to hide() all of them and show that each one on top -
What's the trick? How can I do the same thing in a wpf application? In a typical C# application I would have to open a child window each time but that seems ugly these days.
Thank you in advance!
If you are going to use Pages in WPF, then you will need to read the Navigation Overview page on MSDN. In short however, you can navigate between Pages in a WPF Application by using the NavigationService Class. To change the page from code behind, you could do something like this:
NextPage page = new NextPage();
NavigationService.Navigate(page);
To let the users change the Page, you can use the Hyperlink Class in your Pages:
<Hyperlink NavigateUri="pack://application:,,,/AppName;component/Pages/NextPage.xaml">
Navigate to Next Page
</Hyperlink>
To get your desired page setup, you will have to load your Pages into a Frame, which you can then layout wherever you like in MainWindow.xaml:
<Frame Source="pack://application:,,,/AppName;component/Pages/SomePage.xaml" />
Sounds like you need a custom usercontrol and some databinding.
You can declare DataTemplates in XAML as resources with the model type as key, so that WPF chooses the correct DataTemplate automatically:
Have a main ViewModel, which exposes a ImageSourceViewModel property. This property would either return a CameraSourceViewModel or a FileSourceViewModel, as appropriate.
In your page, the DataContext would be the main ViewModel, and you'd have XAML like this:
Then,
<Page x:Class="Page1"
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:my="clr-namespace:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page1">
<Page.Resources>
<DataTemplate DataType="{x:Type my:CameraSourceViewModel}">
<my:CameraSourceView/>
</DataTemplate>
<DataTemplate DataType="{x:Type my:FileSourceViewModel}">
<my:FileSourceView/>
</DataTemplate>
</Page.Resources>
<Grid>
<ContentControl Content="{Binding ImageSourceViewModel}"/>
</Grid>
I should point out that this example uses the MVVM pattern to allow the viewmodel layer to decide on the content in the middle. Hopefully this is clear enough, if not, give me a shout and I'll try to expand it!
Let's say I have main view model where I've created a CurrentPage property that will tell which page you want to display.
/// <summary>
/// Returns the page ViewModel that the user is currently viewing.
/// </summary>
public ViewModelBase CurrentPage
{
get { return _currentPage; }
private set
{
if (value != _currentPage)
{
if (_currentPage != null)
_currentPage.IsCurrentPage = false;
_currentPage = value;
if (_currentPage != null)
_currentPage.IsCurrentPage = true;
RaisePropertyChanged(() => CurrentPage);
}
}
}
And in your xaml you can bind your page under some control. Let's say I am doing it inside a Border element.
<!-- CURRENT PAGE AREA -->
<Border Background="White" Grid.Column="1" Grid.Row="0">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"
Header="{Binding Path=CurrentPage.DisplayName}" />
</Border>
You can define view to your view model in resources just like this:
(partially complete XAML)
<UserControl x:Class="BAT.View.BATWizardView"
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:view="clr-namespace:BAT.View"
xmlns:viewmodel="clr-namespace:BAT.ViewModel"
mc:Ignorable="d"
d:DesignHeight="350" d:DesignWidth="600">
<UserControl.Resources>
<!-- These four templates map a ViewModel to a View. -->
<DataTemplate DataType="{x:Type viewmodel:MyComparisonViewModel1}">
<view:MyView1 />
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodel:MyComparisonViewModel2}">
<view:MyView2 />
</DataTemplate>
</UserControl.Resources>
<Grid>
<Border Background="White" Grid.Column="1" Grid.Row="0">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"
Header="{Binding Path=CurrentPage.DisplayName}" />
</Border>
</Grid>
</UserControl>
See if that helps.

Categories