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
Related
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());
}
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();
}
}
Struggling to find a solution to this issue that I am having, I have a UWP App that I have been toying around with and for the most part have been able to work things out.
The issue I am facing is with a Hambuger Menu, I have a frame that I load a page up, which displays a gridview of items. When clicked it then displays another page in that frame.
The new page has this which I am able to display data in the page no issue at all.
public sealed partial class CreatureDossier : Page
{
private Species Cat;
public CreatureDossier()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Cat = (Species)e.Parameter;
}
}
In the XAML of this page I am putting a UserControl that I would like to use the data that is present and is where I a struggling. Every time I break on the User Control it shows the DataContext as empty (null).
I have tried
<UIControls:DossierPanel />
Really not understanding what I am needing to do to make this User Control use the DataContext, I should point out that I have this in the UserControl but as stated the DataContext is null.
<TextBlock x:Name="CatName" Text="{Binding Cat.name}" />
Now the interesting thing is that if I use a property called Cat on the Usercontrol with a normal text string and a DependencyProperty in the UserControl code behind, I can display that with this.DataContext = this in the constructor.
User Control code behind
public sealed partial class DossierPanel : UserControl
{
public Models.Species Cat { get { return this.DataContext as Models.Species; } }
public DossierPanel()
{
this.InitializeComponent();
}
}
CreatureDossier.xaml
<TextBlock x:Name="CreatureName" Text="{x:Bind Cat.name}" />
<UIControls:DossierPanel />
Any help in understanding what I need to do would be great.
Set the DataContext of the Page:
public sealed partial class CreatureDossier : Page
{
private Species Cat;
public CreatureDossier()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Cat = (Species)e.Parameter;
DataContext = Cat;
}
}
The UserControl will then inherit the DataContext from the Page (assuming that you don't explicitly set the DataContext property somewhere else) and you should be able to bind to any public property of the Species class like this:
<TextBlock x:Name="CatName" Text="{Binding name}" />
Edit:
{x:Bind} doesn't use the DataContext as a default source: https://learn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension
But if you give the UserControl an x:Name:
<UIControls:DossierPanel x:Name="uc" />
...and add a setter to the Cat property:
public sealed partial class DossierPanel : UserControl
{
public Models.Species Cat { get; set; }
public DossierPanel()
{
this.InitializeComponent();
}
}
And set this one:
public sealed partial class CreatureDossier : Page
{
public CreatureDossier()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
uc.Cat = (Species)e.Parameter;
}
}
<TextBlock x:Name="CatName" Text="{Binding Cat.name, Mode=OneWay}" />
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
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.