Caliburn Micro Conductor.Collection.AllActive not working - c#

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();
}
}

Related

How do you bind an observable collection to a listview located in a modal/window?

I have a wpf application that I want to be able to launch a separate window in which I will have a listview bound to an observable collection. However I am unable to get the collection values to appear in the list view. Here is some of the code.
Window (Named WizardView):
(Data context defined like so at top of xaml):
d:DataContext="{d:DesignInstance Type=viewModels:MainViewModel}"
<Border Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" BorderBrush="Black">
<ListView BorderThickness="0" ItemsSource="{Binding TestModel.FailedTests}">
<Label Content="Introduction" FontWeight="Bold" />
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding }"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
MainViewModel Code:
public class MainViewModel : BaseViewModel
{
public MainViewModel()
{
TestModel = new TestViewModel();
WizardModel = new WizardViewModel(TestModel);
}
private WizardViewModel _wizardModel;
public WizardViewModel WizardModel
{
get
{
return _wizardModel;
}
set
{
_wizardModel = value;
RaisePropertyChanged();
}
}
private TestViewModel _testViewModel;
public TestViewModel TestModel
{
get
{
return _testViewModel;
}
set
{
_testViewModel = value;
RaisePropertyChanged();
}
}
WizardViewModel Code:
public class WizardViewModel : TestViewModel
{
internal TestViewModel TestModel;
public WizardViewModel(TestViewModel testModel)
{
TestModel = testModel;
(TroubleShootCommand is defined in seperate UC, and launches fine)
TestModel.TroubleShootCommand = new DelegateCommand(Load, CanTroubleShoot);
}
public void Load()
{
(Sync Root is used because it is running on worker thread. Issue somewhere here?)
_syncRoot.Send(o =>
{
var troubleShootWizard = new WizardView();
troubleShootWizard.Owner = Application.Current.MainWindow;
troubleShootWizard.ShowDialog();
}, null);
}
Observable Collection in TestViewModel (Initialized in ctor):
private ObservableCollection<string> _failedTests;
public ObservableCollection<string> FailedTests
{
get { return _failedTests; }
set
{
_failedTests = value;
RaisePropertyChanged();
}
}
Any Help is appreciated, I feel like I have tried everything. I have watched values through the watch window under TestModel.FailedTests in the collection right before and right after launch.
First,
(Data context defined like so at top of xaml): d:DataContext="{d:DesignInstance Type=viewModels:MainViewModel}"
This is a mistake, this way d: you are defining the DataContext at design time..
You can create the viewmodel inside .xaml this way:
<WizardView.DataContext>
<viewModels:MainViewModel/>
</WizardView.DataContext>
Using the design time declaration can help in many ways like knowing the viewmodel in case you are creating it and assigning it in C# (or via a IoC mechanism), also it helps tools like IntelliSense and ReSharper to analyze your bindings and warn you if you misspell a property's name in xaml, auto-completion, etc... (more on this can be found here, and here)
Second, if you are assigning the WizardViewModel in your .xaml the same way (i.e. design-time), then you can either do it in your Load() function (add troubleShootWizard.DataContext = this;) or assign it in .xaml the same way I've mentioned before.

Why is my view not updating when adding data from my service to my viewmodel?

So I just created a new project and I am trying to add some data to the collection in my ViewModel, however when I add data to it, it does add but it wont update the UI.
This is where I set the DataContext and where I am trying to add some content to the collection
ProxyService ps;
public MainWindow()
{
InitializeComponent();
DataContext = new BaseViewModel();
ps = new ProxyService();
ps.AcceptConnection();
}
Keep in mind, it does add it to the collection there are no errors I've debugged it and it's infact in the collection.
ProxyServer.cs
public class ProxyService : MessageViewModel
{
public void AcceptConnection()
{
Messages.Add(new MessageModel { Message = "Awaiting connection..." });
Here is the BaseViewModel
public class BaseViewModel
{
public MessageViewModel MessageViewModel { get; set; } = new MessageViewModel();
}
And the MessageViewModel of course
public class MessageViewModel : ObservableObject
{
private ObservableCollection<MessageModel> _messages;
public ObservableCollection<MessageModel> Messages
{
get { return _messages; }
set
{
_messages = value;
OnPropertyChanged();
}
}
public MessageViewModel()
{
Messages = new ObservableCollection<MessageModel>();
}
}
And here is the XAML for the ScrolLViewer to which I am adding the data
<ScrollViewer Height="380"
Margin="10"
>
<StackPanel>
<ItemsControl ItemsSource="{Binding MessageViewModel.Messages}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock FontFamily="Consolas"
Foreground="#61d73d"
Text="{Binding Message}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
The issue I am facing is that it's not updating the UI when it adds something
However! If I add something in the constructor it works just fine. As an example this works just fine, it shows it in the view accoringly
public MessageViewModel()
{
Messages = new ObservableCollection<MessageModel>();
Messages.Add(new MessageModel { Message = "Hello World!" });
}
My best guess is that it's adding to another instance of some sort but I am not sure, I really don't want to have to use singleton because I feel like that will ruin the MVVM pattern.
Three points of note.
You're setting the Window's DataContext to one instance of BaseViewModel, and then creating a separate instance of the ProxyServiceClass.
Your binding for the ItemsControl.ItemsSource should just be binding to a property of the DataContext, in this case Messages.
Don't keep recreating the ObservableCollection - just create it once and add / remove items as required. Any bound control will detect that it implements INotifyCollectionChanged and refresh itself automatically when the collection is updated.
BaseViewModel is one class and ProxyService another one. You can't expect MessageModel objects that you add to the latter affect the former and vice versa. Try to set the DataContext to a BaseViewModel:
public MainWindow()
{
InitializeComponent();
ps = new ProxyService();
ps.AcceptConnection();
DataContext = new BaseViewModel { MessageViewModel = ps };
}

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: TabControl with multiple DataTemplates

I have a TabControl with multiple DataTemplate. the first DataTemplate will be used for search reasons and the second will be for displaying items obtained from that search. My XAML code will be as follows:
<UserControl.Resources>
<!--First template-->
<DataTemplate>
<!-- I will have a DataGrid here-->
</DataTemplate>
<!--Second template-->
<DataTemplate >
<!-- I will have details of one item of the DataGrid-->
</DataTemplate>
</UserControl.Resources>
<TabControl ItemsSource="{Binding }"/>
What I want to accomplish is that in the TabControl the first tab will contain the first DataTemplate (the search template) and when I double click on one row of my DataGrid, a tab will be added with the details of that row (in other words a tab with the second template).
Since I am using MVVM, I thought of creating two UserControls, one for each template and then catch the double click event, but after this I don't know how to add a tab since now my search template is a UserControl seperated from the one that contains the TabControl.
So how do I do this?
UPDATE:
As I read the answers I think I wasn't very clear in stating the problem.
My problem is how to add tabs with the second template, by catching double click events from the first template. I don't have any problem in adding the two templates independently.
Rather can creating two UserControls, you can create and use a DataTemplateSelector in order to switch different DataTemplates in.
Basically, create a new class that inhereits from DataTemplateSelector and override the SelecteTemplate method. Then declare an instance of it in the XAML (much like a value converter), and then apply it to ContentTemplateSelector property of the TabControl.
More info can be found here.
If you're going to do this with MVVM, your tab control should be bound to some ObservableCollection in your VM, and you just add and remove VM's to the collection as needed.
The VMs can be any type you like and your DataTemplates will show the correct view in the tab just like any other view, so yes, create two UserControls for the two views.
public class MainVM
{
public ObservableCollection<object> Views { get; private set; }
public MainVM()
{
this.Views = new ObservableCollection<object>();
this.Views.Add(new SearchVM(GotResults));
}
private void GotResults(Results results)
{
this.Views.Add(new ResultVM(results));
}
}
There are two options: Use datatemplate selector, or use implicit datatemplates and different types for each tabitem.
1. DataTemplateSelector:
public ObservableCollection<TabItemVM> Tabs { get; private set; }
public MainVM()
{
Tabs = ObservableCollection<TabItemVM>
{
new TabItemVM { Name="Tab 1" },
};
}
void AddTab(){
var newTab = new TabItemVM { Name="Tab 2" };
Tabs.Add(newTab);
//SelectedTab = newTab; //you may bind TabControl.SelectedItemProperty to viewmodel in order to be able to activate the tab from viewmodel
}
public class TabItemTemplateSelector : DataTemplateSelector
{
public DataTemplate Tab1Template { get; set; }
public DataTemplate Tab2Template { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var tabItem = item as TabItemVM;
if (tabItem.Name == "Tab 1") return Tab1Template;
if (tabItem.Name == "Tab 2") return Tab2Template;
return base.SelectTemplate(item, container);
}
}
<local:TabItemTemplateSelector
x:Key="TabItemTemplateSelector"
Tab1Template="{StaticResource Tab1Template}"
Tab2Template="{StaticResource Tab2Template}" />
2. Implicit Data Templates:
public class MainVM : ViewModelBase
{
public ObservableCollection<TabItemVM> Tabs { get; private set; }
public MainVM()
{
Tabs = new ObservableCollection<TabItemVM>
{
new Tab1VM(),
};
}
void AddTab()
{
var newTab = new Tab2VM()
Tabs.Add(newTab);
//SelectedTab = newTab;
}
}
public class TabItemBase
{
public string Name { get; protected set; }
}
public class Tab1VM : TabItemBase
{
public Tab1VM()
{
Name = "Tab 1";
}
}
public class Tab2VM : TabItemBase
{
public Tab2VM()
{
Name = "Tab 2";
}
}
<UserControl.Resources>
<!--First template-->
<DataTemplate DataType="local:Tab1VM">
<!-- I will have a DataGrid here-->
</DataTemplate>
<!--Second template-->
<DataTemplate DataType="local:Tab2VM">
<!-- I will have details of one item of the DataGrid-->
</DataTemplate>
</UserControl.Resources>

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.

Categories