How to make TabControl with Caliburn.Micro and diffrent UserControls - c#

i have a problem with TabControl and i dont know how to even start.
I have my root view that i named MainViewModel. Csharp class looks like this:
public class MainWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
//i have couple of ToggleButtons to load diffrent UserControls, LoadAddNewPage
//is one of them
public void LoadAddNewPage() => this.ActivateItem(new AddNewTaskViewModel(params));
}
I Have ToggleButton in MainWindowView.XAML that loading LoadAddNewTaskPage.
<ToggleButton x:Name="LoadAddNewPage"
Grid.Column="4"
Width="50" Height="50"
Content=""
Foreground="White"
BorderThickness="0"
BorderBrush="{x:Null}"
Background="#FF085078" Margin="20,3,0,3" Grid.ColumnSpan="3">
<!-- i deleted data triggers here -->
</ToggleButton>
As you can see above, it loading AddNewTaskViewModel with form to add new item to my database/list (or whatever).
That AddNewTaskView.xaml is simple UserControl with textboxes etc.
My question is, how to prepare my LoadAddNewPage button to load TabControl with two UserControler? For now, i loading new AddNewTaskViewModel() (it is UserControl, it loads properly as i want to ). How to make TabControl with Caliburn.Micro that will store AddNewTaskViewModel and AddNewProjectViewModel? How to make switch in TabControl between two diffrent UserControl? I have a problem to start, because i dont know how to start with this problem. Thanks for any advices
EDIT
Here i will show my full ViewModel
public class MainWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
protected override void OnViewLoaded(object view) => Show.LoginBox(this.loggedUser);
public void LoadUserInfoPage() => this.ActivateItem(new UserInfoViewModel(this.loggedUser));
public void LoadTaskManagerPage() => this.ActivateItem(new TaskManagerViewModel(this.loggedUser, this.repository));
public void LoadNotificationsPage() => this.ActivateItem(new NotificationsViewModel(this.repository));
//here, i want to trigger TabControl with two VMs to choose
public void LoadAddNewTaskPage() => this.ActivateItem(new AddNewTaskViewModel(this.loggedUser, this.repository));
}
EDIT2
I get the context, but i want to achive:
make another Vm class, that will store my User Controls that i want to use in my TabControl:
public class TabControlViewModel
{
//how to store two VMs that i will use to my TabControl here?
}
And in MainViewModel:
public class MainWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
//activate TabControlViewModel that will store AddTaskVM and AddProjectVM
//this vm will display on my `TabControl` in xaml in `MainWindowView.xaml`
public void LoadAddNewPage() => this.ActivateItem(new TabControlViewModel(params));
}

Add a TabControl named "Items" to the view:
<TabControl x:Name="Items" />
...and another Button and method that adds the other type of Screen to the Items collection:
public class MainWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
public void LoadAddNewTask() => this.ActivateItemAsync(new AddNewTaskViewModel());
public void LoadAddNewProject() => this.ActivateItemAsync(new AddNewProjectViewModel());
}

Related

Caliburn Micro Conductor.Collection.AllActive not working

I have tried to activate multiple windows in application using Caliburn Micro with Conductor.Collection.AllActive
Steps Followed:
Inhertited MainHomeViewodel from Conductor.Collection.AllActive
1)created property
public ExploreViewModel Explorer {
get; private set;
}
2) Created ContentControl with name as property name
<ContentControl x:Name="Explorer" />
3)Activated viewmodel with property
Explorer = new ExplorerViewModel();
ActivateItem(Explorer );
After execution of above mentioned code its instantiating ExplorerViewModel but doesnt go to View's constructor or showing View .
Any problem with above implementation or do i need to do anything more to activate item.
Please Help!
Thanks.
EDIT
public class MainHomeWindowViewModel : Conductor<IScreen>.Collection.AllActive
{
protected override void OnInitialize()
{
base.OnInitialize();
ShowExplorer();
}
public void ShowExplorer()
{
Explorer = new ExplorerViewModel();
ActivateItem(Explorer );
}
}
Conductor.Collection.AllActive uses Items property. If you want to display multiple Screens at once, you have to add them to Items property.
Then, because your views are stored in Items property, you want to bind your view to Items. This is an example:
Conductor:
public class ShellViewModel : Conductor<IScreen>.Collection.AllActive
{
public ShellViewModel()
{
Items.Add(new ChildViewModel());
Items.Add(new ChildViewModel());
Items.Add(new ChildViewModel());
}
}
Conductor view (note, because we show collection of items we want to use ItemsSource not ContentControl):
<Grid>
<StackPanel>
<ItemsControl x:Name="Items"></ItemsControl>
</StackPanel>
</Grid>
Child screen:
public class ChildViewModel : Screen
{
}
Child view:
<Grid>
<Border Width="50" Height="50" BorderBrush="Red" BorderThickness="5"></Border>
</Grid>
EDIT: Regarding discussion in comments, here is how you can use IWindowManager to show multiple windows:
public class ShellViewModel : Screen
{
public ShellViewModel(IWindowManager windowManager)
{
var window1 = new ChildViewModel();
var window2 = new ChildViewModel();
windowManager.ShowWindow(window1);
windowManager.ShowWindow(window2);
window1.TryClose();
}
}

Displaying multiple screens in conductor doesn't work

I want to display a couple of screens at the same time. Displaying only one screen works fine, but when I switch my conductor to Conductor<DMChartBase>.Collection.AllActive and add another item, it still renders only one item.
public class DocumentViewModel : Conductor<DMChartBase>.Collection.AllActive
{
public ChartLegendViewModel ChartLegendVm { get; set; }
public DocumentViewModel()
{
ChartLegendVm = new ChartLegendViewModel();
ActivateItem(ChartLegendVm);
}
public void ChangeChart(DMChartBase chart, Column[] columns)
{
ActivateItem(chart);
Items.Last().Init(columns);
Items.Refresh();
}
}
And DocumentView:
<ItemsControl x:Name="Items"></ItemsControl>
I cannot find any reason why this doesn't work. Any ideas?
EDIT:
My code structure looks like this:
public class ShellViewModel : Screen
public class DocumentViewModel : Conductor<DMChartBase>.Collection.AllActive
public class ChartLegendViewModel : ChartDecorator
public abstract class ChartDecorator : DMChartBase
public abstract class DMChartBase : Screen
DocumentView:
<UserControl ...>
<Grid>
<ItemsControl x:Name="Items">
</Grid>
</UserControl>
ChartLegendView:
<UserControl ....>
<ListView>
<ListViewItem Content="First value"></ListViewItem>
<ListViewItem Content="Second value"></ListViewItem>
<ListViewItem Content="Third value"></ListViewItem>
</ListView>
</UserControl>
Bootstrapper:
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<ShellViewModel>()
}
EDIT:
I figured it out!
Previously I wanted to instantiate Chart and Legend separately which is wrong. DocumentViewModel should only be responsible for instantiating ChartDecorator. Inside ChartDecorator I can create as many decorator classes as I want (such as ChartLegendViewModel) and they all get drawn.
IoC isn't used at all? Secondly can you show us bootstrapper and XAML would help alittle. Are these items in a separate library? Normally Conductor uses a Screen or IScreen to have access to the "screens" lifetime. So unless DMChartBase inherits screen(IScreen) I don't think you will get an activation...
//MEF IoC Container -- CM Built-in
[Export(typeof(MainWindowViewModel))]
public class MainWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
[ImportingConstructor]
public MainWindowViewModel([ImportMany]IEnumerable<IScreen> screens)
{
DisplayName = "COmposition Example - CM";
Items.AddRange(screens.Reverse());
}
protected override void OnActivate()
{
ActivateItem(Items[0]);
}
}
as an example... It shows all of them but only one is Active if you change it to AllActive they all would be active "can inter-communicate" but they aren't visible due to constraints of the control, used for this case (tab control) but if it was changed to an itemscontrols you still need to some work since the view wouldn't know how to deal with the binding of the view to the viewmodel

WPF Change View from a User Control MVVM

I'm trying to navigate from one View Model to another without any side panel.
For example, I have a Main Window View, this is where I load my User Control.
I have tried to access the static instance from the MainViewModel to change the Views, but it's not working.
MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type vm:FirstViewModel}">
<v:FirstView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SecondViewModel}">
<v:SecondView/>
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentViewModel}"/>
MainViewModel.cs
class MainviewModel : ObservableObject
{
private ObservableObject _currentViewModel = new FirstViewModel();
public ObservableObject CurrentViewModel
{
get { return _currentViewModel; }
set
{
_currentViewModel = value;
RaisePropertyChangedEvent("CurrentViewModel");
}
}
private static MainViewModel _instance = new MainViewModel();
public static MainViewModel Instance { get { return _instance; } }
}
Here, I have my FirstView, it just contains a button and several other UI designs
FirstView.xaml
<Button Command="{Binding goToSecondView}" />
FirstViewModel.cs
class FirstViewModel : ObservableObject
{
public ICommand goToSecondView
{
get
{
return new DelegateCommand(() =>
{
MainViewModel.Instance.CurrentViewModel = new SecondViewModel();
});
}
}
}
And I have a SecondView, which is similar to FirstView, just that it navigates to FirstView
I have tried searching for a solution, but so far, I have only managed to find examples that shows buttons on a panel which then allow switching of the User Control from clicking those button.
What I am trying to achieve is to enable switching of User Control via the buttons on the User Control itself, without any side panel.
Any help would be very much appreciated and would definitely aid me in my future projects.
Thank You.
You're creating two different instances of MainViewModel. The first one is probably being created by a locator or something and it's the one that your view is binding to when the window is first created. It's then creating a second instance and assigning it to its own static Instance member:
private static MainViewModel _instance = new MainViewModel();
public static MainViewModel Instance { get { return _instance; } }
It's this instance that your ICommand handler is changing when you press the button, which isn't bound to anything. Generally speaking you should be using a dependency injection framework to ensuring it's a true singleton, but for now just do something like this:
public MainViewModel()
{
Instance = this;
}
public static MainViewModel Instance { get; private set; }
Also your code calls RaisePropertyChangedEvent("CurrentViewModel")...I'm pretty sure you meant that to be RaisePropertyChanged("CurrentViewModel").
i would go another way and expose an Event from your first and Second viewmodel like
public event EventHandler<ShowMeEventArgs> ShowMeEvent;
then your main just need to subscribe to this event and can show the viewmodel. and even more your first and second viewmodel dont need to know anything from mainviewmodel

How to make a list of buttons dynamically and show them in the MainForm in a ListBox or ItemsControl

I am new in the WPF.
I want to make the list of buttons from a list of a class say "Buttons" and having two fields (ButtonContent,ButtonID) in the MainForm in WPF.
The idea behind this is that when the MainForm will be loaded then i want to make buttons list dynamically.
The Best Example is already given in the Link
http://www.codeproject.com/Articles/25030/Animating-Interactive-D-Elements-in-a-D-Panel
But i only want to make the equal size buttons stacked horizontally.
How can i do this in WPF? Thank you in advance.
This is the way I would do it. There are some areas here that will need further research on your part, but this will get you started.
First you need your ViewModel. This is the plain old object. It exposes the properties and methods that are pertinent to your business. You mentioned ButtonContent and ButtonID. Let's assume that both of those are strings for now. You'll also need a command for your button I assume.
public class ButtonViewModel : INotifyPropertyChanged
{
private string _content;
public string Content
{
get{ return _content; }
set{ _content = value; OnPropertyChanged("Content"); }
}
// you'll need to implement INotifyPropertyChanged
// also take a look at RelayCommand
// here is your command for you're button
public ICommand ButtonCommand
{
get { return new RelayCommand(execute, canExecute); }
}
private void execute()
{
// my actual command code
}
private bool canExecute()
{
// this will automatically toggle the enabled state on my button
}
}
You'll have one more view model. This will be the DataContext of your MainWindow.
public class AppViewModel
{
public ObservableCollection<ButtonViewModel> MyButtons {get; set; }
public AppViewModel()
{
MyButtons = new ObservableCollection<ButtonViewModel>();
// add some demo data
MyButtons.Add(new ButtonViewModel() {ButtonContent = "Click Me!"});
}
}
Now you need to implement the view. In the MainWindow xaml code. Add your xmlns:local namespace.
<Window.DataContext>
<local:AppViewModel />
</Window.DataContext>
<ListBox ItemsSource="{Binding MyButtons}">
<ListBox.ItemsTemplate>
<DataTemplate>
<Button Command="{Binding ButtonCommand}" Content="{Binding Content}"/>
</DataTemplate>
<ListBox.ItemsTemplate>
</ListBox>
Hopefully this can get you started in the right direction.

Create new ViewB + ViewmodelB in other ViewModelA and show them in ViewA

I use MVVM. I have a ViewA with a Grid in xaml and a ViewModelA. In the ViewModelA I have a Method that looks like this which is called on a Button click:
public void ButtonClickMethod()
{
ViewB viewB = new ViewB();
viewB.DataContext = new ViewBViewModel();
}
How do I add all the created viewBs into the Grid on my ViewA, so I can see them there? Or is my solution wrong in general maybe?
EDIT :
I now used a ObservableCollection<ViewB> in my ViewModelA which notifies on changes in my ViewA xaml at
<ItemsControl ItemsSource="{Binding ObservableCollection<ViewB>}" />
The only problem is the ViewBs shown in the ItemControl should be dragable. Therefore I used expression blend's
Interaction.GetBehaviors(ViewB).Add(new MouseDragElementBehavior() { });
when i create a new ViewB. But it doesn't work.
EDIT2 :
I tried this solution but it doesn't work for me :( Using MouseDragElementBehavior with an ItemsControl and Canvas
Add your Grid an ContentControl and bind it content to your second view.
Here an example with an UserControl.
<ContentControl Grid.Column="0" Margin="5" Content="{Binding SecondView}"/>
Code im ViewModel:
private UserControl secondView;
public UserControl SecondView
{
get
{
return secondView;
}
set
{
this.SetProperty(ref secondView,value);
}
}

Categories