ModernUI ModernTab Link Click - c#

I'm looking for a click event or something similar for use with the ModernUI WPF's ModernTab.
I'm currently using ModernUI WPF (http://mui.codeplex.com/) and I'm trying to use a ModernTab control as a list of employees, which I'll then click and open their details in the Source page.
Problem is, I am going to be creating the list of employees at runtime from a database, which will mean that I'll need to add the links manually. Therefore, I need to be able to hook some click event from the tab so I can find out what user to display- but I don't see a property that'll work. The closest I can see is that the ModernTab parent control has click events, but they only register if I click an empty part of the control.
The only other thing I can think of is to generate a custom panel for each employee at runtime and set it to the Source attribute when I create the list, which I'd rather not do if possible.
Here's my panel:
<UserControl x:Class="Schedule.Pages.EditEmployees"
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:mui="http://firstfloorsoftware.com/ModernUI"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Style="{StaticResource ContentRoot}">
<mui:ModernTab Layout="List" Name="employeeTabList" >
<mui:ModernTab.Links >
<mui:Link DisplayName="Create New..." Source="/Pages/EditEmployeeDetail.xaml" />
</mui:ModernTab.Links>
</mui:ModernTab>
</Grid>
Any help appreciated!

This is just one example of "hijacking" the click on the ModernTab. Here, you can force the content to load in the top frame, for example:
Handle the SelectedSourceChanged event of the ModernTab:
employeeTabList.SelectedSourceChanged += employeeTabList_SelectedSourceChanged;
void employeeTabList_SelectedSourceChanged(object sender, SourceEventArgs e)
{
if (e.Source.OriginalString.EndsWith("EditEmployeeDetail.xaml"))
{
var url = "/Pages/EditEmployeeDetail.xaml";
var bb = new BBCodeBlock();
bb.LinkNavigator.Navigate(new Uri(url, UriKind.Relative), this, NavigationHelper.FrameTop);
// You may want to set some property in that page's ViewModel, for example, indicating the selected User ID.
}
}

Add a fragment to the Uri representing each employee when links are created. Then for the EmployeeDetail usercontrol implement the IContent Interface (specifically OnFragmentNavigation to initialize the data using the fragment). Sample code here -
http://mui.codeplex.com/discussions/455846

Related

Dynamic controls switching in WPF

Title might be misleading but i'm not sure how to describe it.
Lets say i have 2 containers - one on the left, one on the right. Left container has multiple buttons. Pressing them will change whats inside 2nd container.
If i press 1st button a set of buttons and calendar will appear, 2nd - datagridview etc. Its example.
How can i achieve it? I'm not asking for solution (it can't be solved in one line of code, obviously), but what should i search for. Some specific control? Displaying other window inside it? Etc.
I am not sure if I understood the question well, so I wrote the following scenario from what I understood.
As you mentioned, you have a main window that contains 2 panels, one on the left and the other on the right. In the left panel, there is a list of buttons placed as a group of menus, which, when clicked, show other content in the right panel, something like a navigation to another system module (see the gif):
If this is your scenario, you can design your WPF application as follows:
Create UserControls for each screen you want to navigate to. In the previous example, you could create a UserControl for the module of the task list, and another UserControl for the module of My Agenda. Check this link so you know what a UserControl is.
Manage navigation on the main window. Just like in WinForms, you could handle the click event on each button in the left panel, however, an elegant way to handle the click event is that your handle it in the parent container, since, unlike Winforms, the click event is a bubbling event. Check this link, so you know what a routed event and what is a bubbling event.
In the example video, could you notice that each module is in a container that has a header and that the header text changes when the button is clicked and the header text is updated with the button text? This can be done in many ways, but a good way to do it is through data binding, check this link to understand what this concept is. With experience, you will realize when it will be advisable to apply this and when it will not.
As you can see, there are many concepts that you should review and learn to be able to make a good design of an application taking advantage of all the benefits that WPF has and to continue with the philosophy of WPF.
I write an example code that I also publish on GitHub. I explain some things about the code, but I suggest that you expand these concepts in the links that I left you and in other reliable sources of knowledge, such as books or tutorials from Microsoft itself.
The Xaml MainWindow:
<Window
x:Class="WpfApp26.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:local="clr-namespace:WpfApp26"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800" Height="450"
d:DataContext="{d:DesignInstance Type=local:ViewModel}"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<!-- A GroupBox is a control with a header -->
<GroupBox Header="Options">
<!-- Look that the click event is handled in the StackPanel, the container for the buttons -->
<StackPanel Button.Click="ModuleSelected_OnClick">
<Button
Margin="5" Padding="5"
Content="To Do List" Tag="ToDoListModule" />
<Button
Margin="5" Padding="5"
Content="My Agenda" Tag="MyAgendaModule" />
</StackPanel>
</GroupBox>
<!-- The header property is binding to the CurrentModuleName property in the DataContext -->
<GroupBox Name="GbCurrentModule" Grid.Column="1" Header="{Binding CurretModuleName}" />
</Grid>
</Window>
The MainWindow code behind (review the INotifyProperyChanged):
public partial class MainWindow : Window {
private readonly ViewModel vm;
public MainWindow() {
InitializeComponent();
// Setting the Window's DataContext to a object of the ViewModel class.
this.DataContext = this.vm = new ViewModel();
}
private void ModuleSelected_OnClick(object sender, RoutedEventArgs e) {
// The Source property of the RoutedEventArgs gets the Element that fires the event (in this case, the button).
var clickedButton = (Button) e.Source;
this.vm.CurretModuleName = clickedButton.Content.ToString();
// Getting the Tag property of the button.
var tag = clickedButton.Tag.ToString();
// Performing the navigation.
switch (tag) {
case "ToDoListModule":
NavigateToModule(new UcToDoListModule());
break;
case "MyAgendaModule":
NavigateToModule(new UcMyAgendaModule());
break;
}
#region Internal methods
void NavigateToModule(UserControl uc) {
this.GbCurrentModule.Content = uc;
}
#endregion
}
}
The ViewModel class:
// The class implementents the INotifyPropertyChanged interface, that is used
// by the WPF notifications system.
public class ViewModel : INotifyPropertyChanged {
private string curretModuleName;
public string CurretModuleName {
get => this.curretModuleName;
set {
this.curretModuleName = value;
this.OnPropertyChanged();
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
You can use DataTemplates with Data Binding: https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/data-templating-overview
This will allow you to define templates that are automatically applied to objects of specific types. So you could have a calendar object, list view, data grid, etc apply individually.
You could also use the visibility to show/hide the view as desired when your button(s) are clicked.
MVVM frameworks use this quite often: https://compiledexperience.com/blog/posts/using-caliburn-micro-as-a-data-template-selector
Another example https://www.codemag.com/article/0907111/Dressing-Up-Your-Data-with-WPF-DataTemplates
There are also other MVVM approaches that use activators to show/hide/generate new objects of specific types and display them.

How to place an exported view into a specific location of another exported view?

I am trying to create a toolbar by using two exported views. First view has button A, D, E and second view has button B, C. I want to create toolbar by placing both view into a new view. Problem is how to place second View inside first View after Button A.
So, Arrangement of button should be like
A,B,C,D,E (Expected Arrangement)
Here's the working code for placing it side by side i.e.
A,D,E,B,C
This is the user control where both views will be placed
ToolbarView.xaml
<UserControl x:Class="Dummy.Views.ToolbarView"
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:prism="http://prismlibrary.com/"
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width ="0.5*"/>
<ColumnDefinition Width ="0.5*"/>
</Grid.ColumnDefinitions>
<ContentControl prism:RegionManager.RegionName="RegionForFirstView"/>
<ContentControl Grid.Column="1" prism:RegionManager.RegionName="RegionForSecondView"/>
</Grid>
This is the code which will get the exported views and construct Toolbar:
ToolbarView toolbarView = new ToolbarView();
var scopedRegionManager = _myRegionManager.CreateRegionManager();
RegionManager.SetRegionManager(toolbarView, scopedRegionManager);
navParams.Add("regionManager", scopedRegionManager);
scopedRegionManager.RequestNavigate("RegionForFirstView", "ExportedView1", navParams);
scopedRegionManager.RequestNavigate("RegionForSecondView", "ExportedView2", navParams);
ToolbarContent = toolbarView;
The default region adapter for the content control cannot do this.
You have two options:
Create the toolbar region in an items control and make a view for each button and give it an index that's used to put the buttons in correct order
Get the buttons from a custom service that manages the ordering and show them in the content control using a view that contains an items control
If the buttons are static and determined by configuration only, I'd go for option two, because it's trivially easy. If the buttons change dynamically, I guess that the first option makes more sense. If you do not need to remove buttons dynamically, that is, because you cannot un-navigate a view from a region.

How to change Content property in ContentControl

I have custom ContentControl
public class MyContentControl: ContentControl
{....}
with Content defined in XAML like this
<controls:MyContentControl x:Name="myContentControl">
<controls:MyContentControl.Content>
<controls:UserControl1 />
</controls:MyContentControl.Content>
</controls:MyContentControl>
Content shows in designer and in the device when I launch my application. But when I try to change Content property programmatically, for example
UserControl2 control2 = new UserControl2();
myContentControl.Content = control2;
MyContentControl shows nothing. Using standard ContentControl give the same result.
Any suggestions are welcome.
I followed your code to make simple code sample to test. There's no problem.
public class CustomContentControl:ContentControl
{//......}
<Grid>
<local:CustomContentControl x:Name="content">
</local:CustomContentControl>
</Grid>
MyUserControl1 myUserControl1 = new MyUserControl1();
content.Content = myUserControl1;
<UserControl
x:Class="AppContent.MyUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppContent"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<TextBox Text="abc"></TextBox>
</Grid>
You might have done some specific settings in your code. #Martin Zikmund's suggestion also was reasonable. You could refer to his suggestion and check your code. After that, if you still could not solve this issue, please provide a Minimal, Complete, and Verifiable example.
This should work. The reason could be that the control does not stretch and is displayed just 0x0 in size. Try to set absolute Width and Height to the control2 and check if it displays. You can also set myContentControl.HorizontalContentStretch and myContentControl.VerticalContentStretch.
You can try running the app in debugger and then use the Live Property Explorer to see what the actual size of the control inside Content is.
Ok, I found out where the things went wrong. I am using different controls for desktop and mobile devices, so I put some of theirs XAML views to the DeviceFamily-Mobile folder. This way they automatically use when needed. I've confused namespaces, because all XAML views in this folder have a root namespace for accessibility reasons. When I was trying to add control to the ContentControl via c#, I didn't resolve namespace where my controls were placed. So I've put XAML view as a childs to the ContentControl, and they staying invisible as none of them has InitializeComponent() method. Adding correct controls with initialization fixed my problem.
I am very grateful for your answers, they pointed me to the right way.

Detect if parents in VisualTree or LogicalTree has changed in WPF control

I need to know when a parent in the VisualTree or LogicalTree changes of a Control. I need this feature, since anytime a parent changes I need to reevaluate the controls' window class, so that I can attached Command- and InputBindings.
I have a dirty way, which means I need to attach to each parent element and check for parent changes with events, but I was hoping there is another solution.
Example:
I have a UserControl
<UserControl x:Class="WpfApplication21.UserControl1">
<UserControl.CommandBindings>
<CommandBinding Command="ApplicationCommands.Cut" Executed="SomeHandler"></CommandBinding>
</UserControl.CommandBindings>
</UserControl>
and I have a Window that contains one or many UserControls
<Window x:Class="WpfApplication21.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:local="clr-namespace:WpfApplication21"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="SomeHandler"></CommandBinding>
</Window.CommandBindings>
<StackPanel>
<local:UserControl1></local:UserControl1>
<local:UserControl1></local:UserControl1>
</StackPanel>
</Window>
Although in the example I have put the same command, it's only here to show in the example. In reality each control has different commands. The commands of each UserControl need to be merged into the CommandBindings of the Window (the same is true for InputBindings - not shown here). Each UserControl is a plugin that is created from a ViewModel dynamically, so each ViewModel has a different View (the example only shows UserControls, but in reality these a derivation from a UserControl).
For that reason I created a behavior that attaches to a CommandProvider that is implemented by the ViewModel (created by me) so that it's done without code-behind.
Since I have many ViewModels and therefore also Views I need to manage the Command-/InputBindings and attach them to the Window. One problem is that not every View can get a Focus, and somehow for UserControl event if they are focused the Command-/InputBindings don't work.
Is it a little clearer now? I now the situation is a bit complex.
I have a dirty way, which means I need to attach to each parent element and check for parent changes with events, but I was hoping there is another solution.
Sorry to disappoint you but there is no other way, i.e. there is no "AnyParentChanged" event or something that you can hook up to.
You need to somehow iterate through all parent elements and hook up an event handler to an appropriate event for each one of them. It's either this or reconsider your entire approach.
Depending on whether a parent in the visual tree "changes" may not be the best way to solve whatever you are trying to do.

C# User Control that can contain other Controls (when using it)

I found something about this issue for ASP, but it didn't help me much ...
What I'd like to do is the following: I want to create a user control that has a collection as property and buttons to navigate through this collection. I want to be able to bind this user control to a collection and display different controls on it (containing data from that collection).
Like what you had in MS Access on the lower edge of a form ...
to be more precise:
When I actually use the control in my application (after I created it), I want to be able to add multiple controls to it (textboxes, labels etc) between the <myControly> and </mycontrol>
If I do that now, the controls on my user control disappear.
Here is an example of one way to do what you want:
First, the code - UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty MyContentProperty =
DependencyProperty.Register("MyContent", typeof(object), typeof(UserControl1));
public UserControl1()
{
InitializeComponent();
}
public object MyContent
{
get { return GetValue(MyContentProperty); }
set { SetValue(MyContentProperty, value); }
}
}
And the user control's XAML - UserControl1.xaml
<UserControl x:Class="InCtrl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300" Name="MyCtrl">
<StackPanel>
<Button Content="Up"/>
<ContentPresenter Content="{Binding ElementName=MyCtrl, Path=MyContent}"/>
<Button Content="Down"/>
</StackPanel>
</UserControl>
And finally, the xaml to use our wonderful new control:
<Window x:Class="InCtrl.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:InCtrl"
Title="Window1" Height="300" Width="300">
<Grid>
<me:UserControl1>
<me:UserControl1.MyContent>
<Button Content="Middle"/>
</me:UserControl1.MyContent>
</me:UserControl1>
</Grid>
</Window>
I'm having a hard time understanding your question, but I think what you're describing is an ItemsControl using DataTemplates to display the contents of (presumably) an ObservableCollection(T).
A UserControl may not be the best way to do this. You're wanting to add decorations around content, which is basically what Border does: it has a child element, and it adds its own stuff around the edges.
Look into the Decorator class, which Border descends from. If you make your own Border descendant, you should be easily able to do what you want. However, I believe this would require writing code, not XAML.
You might still want to make a UserControl to wrap the buttons at the bottom, just so you can use the visual designer for part of the process. But Decorator would be a good way to glue the pieces together and allow for user-definable content.
Here's a link to a built-in control (HeaderedContentControl) that does the same thing as the accepted answer except that it is an existing control in WPF since .Net 3.0

Categories

Resources