I am using DevExpress' DXCollectionView on MainPage.xaml. I have defined two DataTemplates with a custom control like so:
<ContentPage.BindingContext>
<vm:MyViewModel />
</ContentPage.BindingContext>
<ContentPage.Resources>
<DataTemplate x:Key="NormalTemplate">
<controls:CustomSwipeContainer SwipeCaption="Pin" SwipeCommand="{Binding MyCommand1}"/>
</DataTemplate>
<DataTemplate x:Key="PinnedTemplate">
<controls:CustomSwipeContainer SwipeCaption="Unpin" SwipeCommand="{Binding MyCommand2}"/>
</DataTemplate>
The custom control is defined as:
<dxcv:SwipeContainer.StartSwipeItems>
<dxcv:SwipeItem
x:Name="MyStartSwipeItem"
Caption="{Binding SwipeCaption}"
Command="{Binding SwipeCommand}"
Invoked="StartSwipeItem_Invoked" />
</dxcv:SwipeContainer.StartSwipeItems>
The two commands (MyCommand1 and MyCommand2) are defined in MainPage.cs while StartSwipeItem_Invoked is defined in the custom control.
The SwipeCaption appears as expected. However, when StartSwipeItem_Invoked is executed, the SwipeCommand is null.
How can I bind the two commands?
(Being new to MAUI, I am not sure if this is the best approach so I am open to suggestions.)
Related
I am relatively new to WPF and I'm trying to create a system where you can click a button to add a new tab which generates a corresponding grid to display content. For example, when you open a new tab, the first page would be "Home page", you then have options within this new tab to navigate to other content such as "Options". You can then add another tab and repeat the process etc. IT would be like Google Chrome how each tab represents another browser experience.
So far I have my tabs working, however I am stuck with how I am supposed to generate grids for each tab. Does anyone have any suggestions on how I could do this?
Create an abstract TabData base class with a Header string property.
Create a class that derives from TabData for each type of tab that you want (e.g. HomeData, OptionsData, etc.)
Create a view model with an Items property of type IEnumerable<TabData>.
Bind the ItemsSource property of the TabControl to Items and define an implicit DataTemplate for each concrete TabData type:
<TabControl ItemsSource="{Binding Items}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.Resources>
<DataTemplate DataType="{x:Type local:HomeData}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type local:OptionsData}">
...
</DataTemplate>
</TabControl.Resources>
</TabControl>
The DataTemplate defines the appearance of a tab and may contain a DataGrid or any other element.
I am wrapping Windows Form legacy control inside WPF control , I am doing this for a few Windows Form controls. ie:
// WinFormHost control, only AWrapper is shown here, but there
//are BWrapper, CWrapper etc
public class AWrapper : WindowsFormsHost
{
Child=new ALegacyWinFormControl();
}
//WPF control, similarly there are BControl, CControl etc
<UserControl x:Class="AControl">
<local:AWrapper />
</UserControl>
At my ResourceDictionary I am defining a key for each WPF controls
// each WPF control has a key
<ResourceDictionary>
<DataTemplate x:Key="AControlKey">
<AControl />
</DataTemplate>
<DataTemplate x:Key="BControlKey">
<BControl />
</DataTemplate>
</ResourceDictionary>
There is one layer of duplication that I want to eliminate-- I will have to define each WPF control for each WindowsFormsHost wrapper, which is redundant. Is there anyway that I can do away with the UserControl object, and connect the DataTemplate straight with the WindowsFormsHost control? ie: I am looking at something like this:
// This wouldn't work
<ResourceDictionary>
<DataTemplate x:Key="AControlKey">
<AWrapper/>
</DataTemplate>
<DataTemplate x:Key="BControlKey">
<BWrapper/>
</DataTemplate>
</ResourceDictionary>
But the above doesn't seem to work. Anyway to make the above work? Is it possible at all?
Edit:
This should work provided that AWrapper is a WindowsFormsHost:
<DataTemplate x:Key="AControlKey">
<local:AWrapper/>
</DataTemplate>
Sample usage:
<ContentControl Content="." ContentTemplate="{StaticResource AControlKey}" />
No UserControl is involved.
local maps to the namespace in which the AWrapper class is defined:
xmlns:local="clr-namespace:WpfApplication1"
I need to load a collection of items as documents in AvalonDock 2.0. These objects inherit from an abstract class, for which I want to render a frame inside the document depending on which subclass are.
This is my XAML:
<ad:DockingManager Background="Gray" DocumentsSource="{Binding Path=OpenProjects}"
ActiveContent="{Binding Path=CurrentProject, Mode=TwoWay}">
<ad:DockingManager.DocumentHeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=OpenProjects/Name}" />
</DataTemplate>
</ad:DockingManager.DocumentHeaderTemplate>
<ad:DockingManager.LayoutItemTemplate>
<DataTemplate>
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type vm:SubclassAViewModel}">
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SubclassBViewModel}">
<Frame Source="Pages/SubclassBProject.xaml" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SubclassCViewModel}">
<Frame Source="Pages/SubclassCProject.xaml" />
</DataTemplate>
</Grid.Resources>
</Grid>
</DataTemplate>
</ad:DockingManager.LayoutItemTemplate>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane>
</ad:LayoutDocumentPane>
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
So far I've achieved to show as many documents as items are in the OpenProjects collection, but I can't seem to show anything inside each document.
Plus, I don't know if I'm using ActiveContent properly: I want to assign to CurrentProject the ViewModel assigned on the current active document.
Thank you for your time.
The reason why you are not able to see any content is of the way you defined your LayoutItem templates. This can't work.
Also consider to use a custom control instead of the Frame. The Frame is very heavy. Unless you don't need to display HTML, avoid this control. Content navigation is very easy to implement, in case you want to show navigable content. Just wrap your document content into a UserControl.
You are using the ActiveContent property properly.
To fix your problem you have three recommended solutions, where the first doesn't exactly meet your requirements. Since you defined the DockingManager.LayoutItemTemplate wrong, I will show it anyway.
Solution 1: Local LayoutItemTemplate
In case you only need a single template for all LayoutDocument and LayoutAnchorable containers, you can use the DockingManager.LayoutItemTemplate property. This property accepts a single DataTemplate. Nested DataTemplate definitions, like in your code, are generally not supported by WPF.
<ad:DockingManager>
<ad:DockingManager.LayoutItemTemplate>
<DataTemplate>
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
</ad:DockingManager.LayoutItemTemplate>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane />
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
Solution 2: Implicit DataTemplate
In more advanced scenarios you display different views based on different models. If the displayed content depends on the data type of the model alone (like in your case), the recommended approach is to provide implicit DataTemplate definitions.
WPF will automatically apply an implicit DataTemplate to every data type that matches the DataTemplate.TargetType of this template.
The DataTemplate is implicit, if it has no explicit x:Key value assigned. To ensure that the DataTemplate can actually be applied automatically, the DataTemplate must also be defined in the same resource scope as the target type. E.g., defining the DataTemplate in Application.Resources of App.xaml, would make the template to be applied automatically in the application scope.
<ad:DockingManager>
<ad:DockingManager.Resources>
<DataTemplate DataType="{x:Type vm:SubclassAViewModel}">
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SubclassBViewModel}">
<Frame Source="Pages/SubclassBProject.xaml" />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SubclassCViewModel}">
<Frame Source="Pages/SubclassCProject.xaml" />
</DataTemplate>
</ad:DockingManager.Resources>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane>
</ad:LayoutDocumentPane>
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
Solution 3: DataTemplateSelector
The previous solution, which uses implicit DataTemplate definitions, can be replaced with a DataTemplateSelector. DataTemplateSelector is another WPF concept to apply a DataTemplate selectively.
A DataTemplateSelector is the recommended choice, if selecting a DataTemplate may depend on more complex constraints, than the model's data type alone. It allows to e.g. evaluate the data item and chose the appropriate template based on certain criteria.
To define a template selector, you have to extend DataTemplateSelector, which is expected to return a DataTemplate.
The easiest way is to define the templates in App.xaml resource dictionary using an x:Key and then select from them based on the condition.
DockingManger accepts a template selector by assigning the DockingManager.LayoutItemTemplateSelector property:
App.xaml
Define the explicit DataTemplate using x:Key:
<Application.Resources>
<DataTemplate x:Key="SubclassAViewModelTemplate" DataType="{x:Type vm:SubclassAViewModel}">
<Frame Source="Pages/SubclassAProject.xaml" />
</DataTemplate>
<DataTemplate x:Key="SubclassBViewModelTemplate" DataType="{x:Type vm:SubclassBViewModel}">
<Frame Source="Pages/SubclassBProject.xaml" />
</DataTemplate>
<DataTemplate x:Key="SubclassCViewModelTemplate" DataType="{x:Type vm:SubclassCViewModel}">
<Frame Source="Pages/SubclassCProject.xaml" />
</DataTemplate>
</Application.Resources>
DocumentManagerTemplateSelector.cs
The following code uses the Switch Expression, which is available since C# 8.0. It can be replaced with a switch statement or cascaded if-statements.
class DocumentManagerTemplateSelector : DataTemplateSelector
{
#region Overrides of DataTemplateSelector
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return item switch
{
SubclassAViewModel _ => Application.Current.Resources["SubclassAViewModelTemplate"] as DataTemplate,
SubclassBViewModel _ => Application.Current.Resources["SubclassBViewModelTemplate"] as DataTemplate,
SubclassCViewModel _ => Application.Current.Resources["SubclassCViewModelTemplate"] as DataTemplate,
_ => base.SelectTemplate(item, container)
};
}
#endregion
}
MainWindow.xaml
<ad:DockingManager>
<xcad:DockingManager.LayoutItemTemplateSelector>
<local:DocumentManagerTemplateSelector />
</xcad:DockingManager.LayoutItemTemplateSelector>
<ad:LayoutRoot>
<ad:LayoutPanel>
<ad:LayoutDocumentPaneGroup>
<ad:LayoutDocumentPane>
</ad:LayoutDocumentPane>
</ad:LayoutDocumentPaneGroup>
</ad:LayoutPanel>
</ad:LayoutRoot>
</ad:DockingManager>
I'm using Syncfusion's DockingManager with the Adapter for MVVM so a List of ViewModels which implement IDockElement can be bound.
I'm using it this way:
<dm:DockingAdapter Grid.Row="1" ItemsSource="{Binding DockingItems}" />
In addition I'm using the latest version of Catel and its Custom controls.
The MVVM Adapter of the DockingManager needs these entries, to create a view from a ViewModel:
<DataTemplate DataType="{x:Type local:EventEditorViewModel}">
<Grid>
<events:EventEditorControl/>
</Grid>
</DataTemplate>
The problem is, that my EventEditorControl has a parameter in its constructor which needs the ViewModel.
How do I pass the correct ViewModel?
You cannot pass parameters to constructors using xaml.
Make you parameter a dependency property:
<events:EventEditorControl MyParameterAsDependencyProperty="{Binding SomeProperty}" />
What would be the best way to build a data-navigation like in access-forms in XAML/C#?
Should I build a user control (or even custom control) that I just bind to my collection in which I can put other controls? (hence this question: C# User Control that can contain other Controls (when using it) )
Or can I build something by deriving from then ItemsControl somehow? how?
Or would this be done completely different today (like "this style of navigation is so last year!")?
I'm relatively new to C# and all (not programming as such, but with more like "housewife-language" Access-VBA) also I'm no native english speaker. So pls be gentle =)
You can create user control and place a bunch of buttons (First, Prev, Next, Last, etc..) in it and place it on the main window. Secondly, you can bind your data navigation user control to a CollectionViewSource which will help you to navigate among your data.
Your main window:
<Window.Resources>
<CollectionViewSource x:Key="items" Source="{Binding}" />
</Window.Resources>
<Grid>
<WpfApplication1:DataNavigation DataContext="{Binding Source={StaticResource items}}" />
<StackPanel>
<TextBox Text="{Binding Source={StaticResource items},Path=Name}" />
</StackPanel>
</Grid>
Your Data Navigation User Control:
<StackPanel>
<Button x:Name="Prev" Click="Prev_Click"><</Button>
<Button x:Name="Next" Click="Next_Click">></Button>
<!-- and so on -->
</StackPanel>
And your click handlers goes like this:
private void Prev_Click(object sender, RoutedEventArgs e)
{
ICollectionView view = CollectionViewSource.GetDefaultView(DataContext);
if (view != null)
{
view.MoveCurrentToPrevious();
}
}
I hope this helps.
Sounds like you're after a DataGrid control. Microsoft is releasing a WPF DataGrid as part of a WPF Toolkit which you can download here: http://wpf.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=25047.