I am new to WPF and i am using WPF Model-View-ViewModel Toolkit 0.1 to get my hands on WPF.
I have a fairly simple question but i cant get my head around it.
How do i display new view from the menu item on the mainview?
This is how my code looks like:
MainView.xaml
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Command="{Binding NewPage}" Header="New Page"
InputGestureText="Ctrl-N" />
</MenuItem>
</Menu>
MainViewModel.cs
private DelegateCommand newPageCommand;
public ICommand NewPage
{
get
{
if (newPageCommand == null)
{
newPageCommand = new DelegateCommand(GoToNewPage);
}
return newPageCommand;
}
}
private void GoToNewPage()
{
???
}
What do i write in the GoToNewPage to display the newPage.xaml?
Usually your application is run entirely in the ViewModels, and Views are used to allow users to interact with the ViewModels in a friendly manner.
In your case, the ViewModel might have a property called CurrentPage, which is bound to a ContentControl.Content in your View. To change pages, the GoToNewPage command would set the CurrentPage property to a NewPageViewModel.
This would cause the ContentControl to realize it's binding has changed, and in the process of updating the binding, it would realize that the Content has changed and it needs to use a new DataTemplate to draw that content.
<ContentControl Content="{Binding CurrentPage}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:HomePageViewModel}">
<local:HomePageView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:NewPageViewModel}">
<local:NewPageView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
I have a simple example of this here if you're interested
Related
I am still somewhat new to the concept of MVVM and I need to use NavigationView and apply mvvm. Is there any easy way to convert this to navigationview?
I have this code from an mvvm tutorial
<Grid MaxWidth="600" Margin="20 10">
<Grid.Resources>
<DataTemplate DataType="{x:Type vms:MakeReservationViewModel}">
<views:MakeReservationView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:ReservationListingViewModel}">
<views:ReservationListingView />
</DataTemplate>
</Grid.Resources>
<ContentControl Content="{Binding CurrentViewModel}" />
</Grid>
I understand that I can use NavigationView ItemInvoked method but I need some guidance as to how to call the ViewModel from the ItemInvoked method
private void NavView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
{
ContentFrame.Navigate(typeof(SettingsPage));
}
else
{
// find NavigationViewItem with Content that equals InvokedItem
var item = sender.MenuItems.OfType<NavigationViewItem>().First(x => (string)x.Content == (string)args.InvokedItem);
NavView_Navigate(item as NavigationViewItem);
}
}
private void NavView_Navigate(NavigationViewItem item)
{
switch (item.Tag)
{
case "MakeReservation":
ContentFrame.Navigate(typeof(MakeReservationPage));
break;
case "ReservationList":
ContentFrame.Navigate(typeof(ReservationListPage));
break;
}}
How do I navigate to the ViewModel?
EDIT
xmlns:ui =“http://schemas.modernwpf.com/2019”
<Grid>
<ui:NavigationView x:Name=“NavView” ItemInvoked=“NavView_ItemInvoked”>
<ui:NavigationView.MenuItems>
<ui:NavigationViewItem Content = “Make Reservation” Tag=“MakeReserve”/>
<ui:NavigationViewItem Content = “Reservation List” Tag=“List”/>
</ui:NavigationView.MenuItems>
<ui:Frame x:Name=“ContentFrame”>
<ui:Frame.ContentTransitions>
<ui:TransitionCollection>
<ui:NavigationThemeTransition/>
</ui:TransitionCollection>
</ui:Frame.ContentTransitions>
</ui:Frame>
</ui:NavigationView>
I am able to change the view without a problem but I wanted to change it so that when I select one, it will navigate to the viewmodel?
I'm quite new to WPF/MVVM and have a lot to learn still, but I'm hitting an issue at the moment that i can't seem to find good answers for. Most probably because I'm asking the wrong questions.
What I have:
I have a main form with some buttons which load user controls. One of these user controls contains a TabControl.
This TabControl has a manually populated first Tab, which I've excluded from below snippet, but all other tabs should be populated with another user control, which will load database data depending on its viewmodel constructor.
XAML:
<TabItem Header="Two"
Name="Two"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
<ContentControl cal:View.Model="{Binding LoadedControl}"></ContentControl>
</TabItem>
<TabItem Header="Three"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
<ContentControl cal:View.Model="{Binding LoadedControl}"></ContentControl>
</TabItem>
<TabItem Header="Four"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
<ContentControl cal:View.Model="{Binding LoadedControl}"></ContentControl>
</TabItem>
C#
private DocumentTemplateControlViewModel _loadedControl;
public DocumentTemplateControlViewModel LoadedControl
{
get { return _loadedControl; }
set
{
if (value == _loadedControl)
return;
_loadedControl = value;
NotifyOfPropertyChange(() => LoadedControl);
}
}
public int SelectedTabIndex
{
get
{
return _selectedTabIndex;
}
set
{
Task.Run(() => LoadData());
_selectedTabIndex = value;
LoadedControl = new DocumentTemplateControlViewModel(Templates, _selectedTabIndex);
}
Now, This works as I intended it to work for Tab Two, but if I add the same line of
<ContentControl cal:View.Model="{Binding LoadedControl}">
to Tab Three,Four,etc.. (as I've done in the XAML snippet above) it will only work on the last tab that I've added the binding to, and Tab two,three will be blank.
I also tried to achieve the same thing with Caliburn Micro ActivateItem but this means i can also only declare one ActiveItem in my TabControl XAML as well.
TLDR: What is the best way to dynamically show a new user control viewmodel within a tabitem?
Thanks so much
Derive your view model from Conductor<IScreen>.Collection.OneActive and add the DocumentTemplateControlViewModel objects that you want to bind to the tabs to the Items property:
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
{
public ShellViewModel()
{
Items.Add(new DocumentTemplateControlViewModel { DisplayName = "1" });
Items.Add(new DocumentTemplateControlViewModel { DisplayName = "2" });
Items.Add(new DocumentTemplateControlViewModel { DisplayName = "3" });
}
}
DocumentTemplateControlViewModel must implement IScreen and the easiest way to do this is to derive from Screen:
public class DocumentTemplateControlViewModel : Screen
{
}
In the XAML you could then simply add a TabControl with a ContentTemplate, e.g.:
<TabControl Name="Items">
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
You may replace the TextBlock with a UserControl or any other UI element.
How can I trigger routed commands implemented inside a UserControl which is nested inside a ContentControl?
What I basically have is an outer view (derived from UserControl) which contains:
1) A button which should trigger the command MyCommand:
The CommandTarget is obviously wrong here, as it should be the view which is hosted inside the ContentControl, and not the content control itself, as the CommandBinding is added to the CommandBindings collection of InnerView.
<Button Command="{x:Static Commands:MyCommands.MyCommand}" CommandTarget="{Binding ElementName=ViewHost}">
Trigger Command
</Button>
2) A ContentControl. The Content property is bound to the ViewModel which should be used by the inner view:
<ContentControl x:Name="ViewHost" Content="{Binding InnerViewModel}" />
3) A DataTemplate which defines the type of the inner view:
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type ViewModels:InnerViewModel}">
<Views:InnerView />
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
InnerView (derived from UserControl) sets a CommandBinding in it's Loaded event:
public partial class InnerView : UserControl
{
private void InnerViewLoaded(object sender, System.Windows.RoutedEventArgs e)
{
view.CommandBindings.Add(new CommandBinding(MyCommands.MyCommand, this.ExecuteMyCommand, this.CanExecuteMyCommand));
}
}
And of course a class which defines the command:
internal class MyCommands
{
static MyCommands()
{
MyCommand = new RoutedCommand("MyCommand", typeof(MyCommands));
}
public static RoutedCommand MyCommand { get; private set; }
}
How can I get this working? The problem is probably that the CommandTarget on the Button is wrong. How can I bind the CommandTarget to the control hosted by the ContentControl?
If I put InnerView directly into OuterView and set the Button's CommandTarget to the InnerView instance, it works:
<Views:InnerView x:Name="InnerViewInstance" />
<Button Command="{x:Static Commands:MyCommands.MyCommand}" CommandTarget="{Binding ElementName=InnerViewInstance}">
Trigger Command
</Button>
Try this
<UserControl.Resources>
<ResourceDictionary>
<Views:InnerView x:Key="innerView"/>
<DataTemplate DataType="{x:Type ViewModels:InnerViewModel}">
<ContentControl Content="{StaticResource innerView}" />
</DataTemplate>
</ResourceDictionary>
<Button Command="{x:Static Commands:MyCommands.MyCommand}" CommandTarget="{StaticResource innerView}">
Trigger Command
</Button>
I havent tested it but hope this will help you. though this seems a very complex issue.
I ran into this issue and learned that I had to register a command type dependency property for each user control within my user control hierarchy.
I learned this my another link on this site.
I have three Text Box called TxtDocumentTitle1, TxtDocumentTitle2,TxtDocumentTitle3 lastly there is a Add More Button. Client can Click Add more Button so that it generates Text box naming TxtDocumentTitle4. If more needed he/she can Add more Text Boxes.
Sample XAML code of View
<Grid Height="450" Width="700" Background="White">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="67,20,0,0" Name="textBlocKname" Text="Document Title1:" VerticalAlignment="Top" Width="110" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="67,87,0,0" Name="textBlockAddress" Text="Document Title2:" VerticalAlignment="Top" Width="110" />
<TextBlock Height="23" HorizontalAlignment="Left" Margin="67,154,0,0" Name="textBlockCompanyName" Text="Document Title3:" VerticalAlignment="Top" Width="110" />
<TextBox Height="46" Margin="67,37,87,0" Name="txtDocumentTitle1" VerticalAlignment="Top" FontSize="24" />
<TextBox Height="46" HorizontalAlignment="Left" Margin="67,106,0,0" Name="txtDocumentTitle3" VerticalAlignment="Top" Width="546" FontSize="24" />
<TextBox Height="46" HorizontalAlignment="Left" Margin="67,171,0,0" Name="txtDocumentTitle2" VerticalAlignment="Top" Width="546" FontSize="24" />
<Button Content="Add More" Height="37" HorizontalAlignment="Right" Margin="0,223,87,0" Name="btnAddmore" VerticalAlignment="Top" Width="102" />
</Grid>
You can achieve this easily via Binding. if your Window does not have a ViewModel open your window's xaml.cs and make it like this:
public Window1()
{
InitializeComponent();
DataContext = this;
}
public ObservableCollection<TextBoxVm> Items { get { return _items; } }
private ObservableCollection<TextBoxVm> _items = new ObservableCollection<TextBoxVm>();
if not, just add the two last lines to the viewModel of your window.
Now you need to define a class derived from DependencyObject and name it say TextBoxVm. create two DependencyPropertys in it (use propdp snippet) as follows:
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TextBoxVm), new UIPropertyMetadata("default text",
(d,e)=>
{
var vm = (TextBoxVm)d;
var val = (string)e.NewValue;
MyDataService.FindAndUpdateItemInDatabase(vm.Id, val);//you can access database with something like this
}));
public string TitleText
{
get { return (string)GetValue(TitleTextProperty); }
set { SetValue(TitleTextProperty, value); }
}
public static readonly DependencyProperty TitleTextProperty =
DependencyProperty.Register("TitleText", typeof(string), typeof(TextBoxVm), new UIPropertyMetadata("default title"));
This would be the xaml code:
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding TitleText}"/>
<TextBox Text="{Binding Text}"/>
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now the only thing left is to write Button logic. simply add TextBoxVm to Items when Button is clicked.
Items.Add(new TextBoxVm {
TitleText = string.Format("Document Title{0}:", Items.Count+1)
});
Edit Note:
this approach is standard MVVM (expect for the button click event, which should be done using Command). So if you want to add controls in code (which is not recommended) search this :
add control to wpf grid programmatically.
*Above Answer from Bizz Gives Solution of My Question * Beside that it Rise me a Question about *DependencyObject * after Few Research i found this about Dependancy Object which may be Helpful for New comer to WPF like me :)
What is DependencyObject??
Dependency object is the base object for all WPF objects. All the UI Elements like Buttons TextBox etc and the Content Elements like Paragraph, Italic, Span etc all are derived from Dependency Object.
Dependency objects are used for WPF property system. By default, what ever the property system we have in DOT Net CLR is very basic. But Dependency properies provide lots of addtional features/services to support Data Binding.
Once you create any property as a dependency property, then automatically you get following feature implemented for you. ie. Change Notification, Validation, Call Back, Inheritance, DataBinding, Styles, Default Values etc.
If you need to implement all these features on your own for all properties where you need these feature, then it will be a big process and head ache for you. So, these all coming out of the box from Dependency Object class.
Basically dependency object class contains a dictionary. So, when ever set any value or retrieve value, then it will change the value or read from that Dictionary. So, it is nothing but a key value pair.
For Detail Info abouT DependencyObject
http://www.codeproject.com/Articles/140620/WPF-Tutorial-Dependency-Property
http://www.pinfaq.com/32/what-is-dependency-object-in-wpf-where-should-i-use-it
I want to have a listbox, that allows the user to fetch lets say 20 items from the DB and displays a hint on the last row of the listbox if there are more items to be fetched. When the user clicks on this last row, additional items should be retrieved from the DB, until there aren't any more and the last line displays this information.
First:
listitem1
listitem2
...
listitem19
listitem20
Button: <get_more>
after button press:
listitem1
listitem2
...
listitem39
listitem40
Info: <no more items>
Could all this be done in XAML only?
What would be the best solution to implement this?
Dude -- Everything can be done with XAML :D
Following a MVVM approach, I'd recommend you to do the following:
1/ Getting started: A DockPanel
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Bottom" />
<ListBox />
</DockPanel>
2/ Bind your ListBox to an ObservableCollection in your ViewModel:
<ListBox ItemsSource="{Binding ListElements}" />
In the ViewModel:
private ObservableCollection<String> _listElements;
public ObservableCollection<String> ListElements
{
get { return _listElements; }
set { _listElements = value; }
}
3/ Bind your Button's content to a predefined String:
<Button Content="{Binding ButtonString}" />
In the ViewModel:
public String ButtonString
{
get
{
//There, define if there are any more things to display
}
}
4/ Your Button fires a Command launching a method, let's say GetMore() :
<Button Content="{Binding ButtonString}" Command="{Binding GetMoreCommand} />
In ViewModel:
private void GetMore()
{
//append to the _listElements new elements from the list
//Update the ButtonString if there are no more elements
}
And there you go!
(you can also, if needed, define a button removing things from the ObservableCollection for example)