My app is utilizing a SplitView which has a Frame as it's content. I can't seem to figure out how to use the buttons in my split view to change pages in the frame. Right now i'm trying to bind the SourcePageType to my view model, but that doesn't work. Here is my setup.
Frame
<SplitView.Content>
<Frame x:Name="frame" SourcePageType="{Binding FrameSource}">
<Frame.ContentTransitions>
<TransitionCollection>
<NavigationThemeTransition>
<NavigationThemeTransition.DefaultNavigationTransitionInfo>
<EntranceNavigationTransitionInfo/>
</NavigationThemeTransition.DefaultNavigationTransitionInfo>
</NavigationThemeTransition>
</TransitionCollection>
</Frame.ContentTransitions>
</Frame>
</SplitView.Content>
View Model
private string frameSource;
public string FrameSource
{
get { return frameSource; }
set
{
frameSource = value;
RaisePropertyChanged("FrameSource");
}
}
private RelayCommand<string> navCommand;
public RelayCommand<string> NavCommand
{
get
{
navCommand = new RelayCommand<string>(ExecuteNav);
return navCommand;
}
}
public void ExecuteNav(string page)
{
FrameSource = page;
}
I'm using MVVM Light for my framework. What is the best way to do this?
I also have been struggling with this using mmvm light and came up with this method which I use in the main window with a content control bound to the selected viewmodel I want to show. It may be somewhat overkill, but it works and its not too difficult to maintain.
In the main page view model I create a menu object:
private void constructMenu()
{
MenuMessages = new ObservableCollection<MenuMessage>();
MenuMessages.Add(new MenuMessage
{
menutext = "FirstPage",
isactive = true,
newWindow = false,
viewModelName = "FirstPageViewModel"
});
MenuMessages.Add(new MenuMessage
{
menutext = "2page",
isactive = true,
newWindow = false,
viewModelName = "2pageViewModel"
});
I have the following INotifyable properties:
public MenuMessage selectedmenuitem
public ObservableCollection<MenuMessage> MenuMessages
public Object selectedViewModel
in addition each view model that I use is an INotifyable propery
public FirstPageViewModel firstpageviewmodel;
public 2PageViewModel firstpageviewmodel;
My main page xaml looks like this:
<Window
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:Myproj"
xmlns:Views="clr-namespace:Myproj.Views"
xmlns:vm="clr-namespace:Myproj.ViewModel"
x:Class="Myproj.MainWindow" mc:Ignorable="d"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<DataTemplate DataType="{x:Type vm:FirstPageViewModel}">
<Views:FirstPageView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:2PageViewModel}">
<Views2pageView/>
</DataTemplate>
</Window.Resources>
<DockPanel LastChildFill="True">
<StackPanel DockPanel.Dock="Top" >
<ListView ItemsSource="{Binding MenuMessages}" SelectedItem="{Binding selectedmenuitem}" >
<ListView.ItemsPanel>
<ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/></ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type MenuItem}" >
<TextBlock Text="{Binding menutext}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<ContentControl Content="{Binding selectedVM}" ></ContentControl>
</DockPanel>
In the viewmodel I call the following method after the RaisePropertyChanged of the selectedmenuitem setter:
private void switchviewmodel()
{
switch (selectedmenuitem.viewModelName)
{
case "FirstPageViewModel":
selectedVM = irstpageviewmodel;
break;
case "2PageViewModel":
selectedVM = 2pageviewmodel;
break;
}
}
Related
I have a user control like this
<UserControl
x:Class="App41.UserCntrl"
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" mc:Ignorable="d">
<Grid>
<ListView SelectionMode="Multiple" x:Name="lview" ItemsSource="{x:Bind ItemsSource, Mode=OneWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{x:Bind PropertyNameToBind, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
code behind
public sealed partial class UserCntrl : UserControl
{
public UserCntrl()
{
this.InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(UserControl),
new PropertyMetadata(null));
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public string PropertyNameToBind
{
get { return (string)GetValue(PropertyNameToBindProperty); }
set { SetValue(PropertyNameToBindProperty, value); }
}
public static readonly DependencyProperty PropertyNameToBindProperty =
DependencyProperty.Register("PropertyNameToBind", typeof(string), typeof(UserControl), new PropertyMetadata(""));
}
And I am calling this user control in my main page like this
<Page
x:Class="App41.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App41" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d">
<Grid>
<local:UserCntrl x:Name="lview" Loaded="EditTextControl_Loaded"></local:UserCntrl>
</Grid>
Code behind
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void EditTextControl_Loaded(object sender, RoutedEventArgs e)
{
ObservableCollection<OptionItem> io = new ObservableCollection<OptionItem>();
io.Add(new OptionItem { Name = "11111111111" });
lview.ItemsSource = io;
lview.PropertyNameToBind = "Name";
}
}
public class OptionItem
{
private string _Name = string.Empty;
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}
All looks good to me, but ListView displays empty items instead of my content. I believe the issue is in this line
<TextBlock Text="{x:Bind PropertyNameToBind, Mode=OneWay}" />
Where I am trying to bing Name property inside the OptionItem Model. How can I solve this?
In UWP, each DataTemplate corresponds to an item in the ItemsSource, so the DataContext of the DataTemplate is limited to the item itself, specific to your project, is OptionItem.
You use {x:Bind PropertyNameToBind} is actually trying to bind OptionItem.PropertyNameToBind, but your OptionItem does not have this property, so nothing will be displayed.
Please determine whether you want to bind PropertyNameToBind or OptionItem.Name property, if it is OptionItem.Name, please use {x:Bind Name}.
If not, try this:
<UserControl
...
x:Name="Main">
<Grid>
<ListView SelectionMode="Multiple" x:Name="lview" ItemsSource="{x:Bind ItemsSource, Mode=OneWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ElementName=Main,Path=PropertyNameToBind}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>
Update
I think I understand what you mean, but you have some misunderstandings about binding.
What you bind is a property, and you pass a string value. This value cannot be used as a property name to reflect the properties of the binding class.
Combined with your needs, I recommend that you use the interface for dynamic processing:
interface
public interface ITest
{
string GetDisplayText();
}
class
public class OptionItem : ITest
{
public string Name { get; set; } = string.Empty;
public string GetDisplayText()
{
return Name;
}
}
control
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ITest">
<TextBlock Text="{x:Bind GetDisplayText()}" />
</DataTemplate>
</ListView.ItemTemplate>
By creating an interface, a method for outputting displayed text is provided.
Any class that inherits this interface can override this method according to their needs.
Inside the control, you only need to call the GetDisplayText() method, regardless of which class is bound.
<Grid>
<ListView SelectionMode="Multiple" x:Name="lview" ItemsSource="{x:Bind ItemSource, Mode=OneWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{x:Bind OptionItem.Name, Mode=OneWay}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Grid>
As a new in WPF and MVVM light, I am struggling to apply the MVVM pattern in a TabControl. I will give you an example of what I am trying to achieve.
TabOne xaml and its view model
<UserControl x:Class="TestTabControl.TabOne"
xmlns:local="clr-namespace:TestTabControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="tab one ..." FontWeight="Bold" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</UserControl>
//TabOne ViewModel
class TabOne : ViewModelBase
{
public string TabName
{
get
{
return "TabOne";
}
}
}
TabTwo xaml and its viewmodel
<UserControl x:Class="TestTabControl.TabTwo"
xmlns:local="clr-namespace:TestTabControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="tab two ..." FontWeight="Bold" FontSize="14" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</UserControl>
//TabTwo ViewModel
class TabTwo : ViewModelBase
{
public string TabName
{
get
{
return "TabTwo";
}
}
}
and finally the MainWindow xaml and its viewmodel
<Window x:Class="TestTabControl.MainWindow"
xmlns:local="clr-namespace:TestTabControl"
mc:Ignorable="d"
Title="Test Tab Control" MinWidth="500" Width="1000" Height="800">
<TabControl ItemsSource="{Binding TabViewModels}" >
<TabControl.ItemTemplate >
<!-- header template -->
<DataTemplate>
<TextBlock Text="{Binding TabName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
?????????
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Window>
//MainWindow ViewModel
class MainWindowViewModel : ViewModelBase
{
private ObservableCollection<ViewModelBase> _tabViewModels;
public MainWindowViewModel()
{
_tabViewModels = new ObservableCollection<ViewModelBase>();
TabViewModels.Add(new TabOne());
TabViewModels.Add(new TabTwo());
}
public ObservableCollection<ViewModelBase> TabViewModels
{
get
{
return _tabViewModels;
}
set // is that part right?
{
_tabViewModels = value;
RaisePropertyChanged(() => TabViewModels);
}
}
}
What am I supposed to write in the DataTemplate? Can I pass both usercontrols for TabOne and TabTwo in this DataTemplate in order to get the view for each tab I click? Or do I need to write another DataTemplate?
You may already knew the answer by now. But for the benefits of other people, what you need to do is:
<Grid Margin="10">
<Grid.Resources>
<DataTemplate DataType="{x:Type local:TabOne}">
<local:UserControlOne/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:TabTwo}">
<local:UserControlTwo/>
</DataTemplate>
</Grid.Resources>
<TabControl Margin="10"
ItemsSource="{Binding TabViewModels}">
</TabControl>
</Grid>
Please note that, your UserControl for TabOne ViewModel is also named TabOne.
I changed it to UserControlOne. Same applies to UserControlTwo.
fellow programmers.
I'm trying to build my first WPF app with tabbed interface. I've decided to not rely on any MVVM framework, since I'm only starting my journey.
I've found quite a few tutorials, but can't make WPF to pick up correct view for viewmodels.
What I have is Shell view, with underlying DataContext with ObservableCollection of my "Tabs viewmodels":
Shell.Xaml
<Controls:MetroWindow x:Class="WpfApplication1.View.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Shell" Height="327" Width="667"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:v="clr-namespace:WpfApplication1.View"
xmlns:vm="clr-namespace:WpfApplication1.ViewModel"
>
<Grid>
<TabControl ItemsSource="{Binding Pages}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.Resources>
<DataTemplate DataType="x:Type vm:FirstPageModel">
<v:FirstPageView />
</DataTemplate>
<DataTemplate DataType="x:Type vm:SecondPageModel">
<v:SecondPageView />
</DataTemplate>
</TabControl.Resources>
</TabControl>
</Grid>
All I with - to get my views inside tab's of tabcontrol. But, no matter where I put my DataTemplate (window.resources, tabcontrol.resources, etc), I only get something like this:
As I can understand - somehow WPF doesn't see my views, but I don't understand why.
My "views" are simple UserControls, like this:
<UserControl x:Class="WpfApplication1.View.FirstPageView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding Message}" Margin="36,131,47,127" />
</Grid>
and corresponding ViewModel is:
public class FirstPageModel:BaseViewModel
{
public override string DisplayName
{
get
{
return "First page";
}
}
public string Message { get { return "Hi from " + DisplayName; } }
}
Could somebody tell me what's wrong with my code?
Edit:
ShellViewModel:
public class ShellViewModel : BaseViewModel
{
public override string DisplayName
{
get
{
return "First";
}
}
private ObservableCollection<BaseViewModel> pages;
public ObservableCollection<BaseViewModel> Pages
{
get{
if(this.pages==null)
{
this.pages = new ObservableCollection<BaseViewModel>{
new FirstPageModel(),
new SecondPageModel()
};
}
return this.pages;
}
}
}
App.xaml.cs:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Shell sh = new Shell();
ShellViewModel shvm = new ShellViewModel();
sh.DataContext = shvm;
sh.Show();
}
}
The DataTemplate definition is incorrect. You must use curly braces for x:Type markup extension with DataType attribute value:
<TabControl ItemsSource="{Binding Pages}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.Resources>
<!-- Use curly braces for x:Type markup extension -->
<DataTemplate DataType="{x:Type vm:FirstPageModel}">
<v:FirstPageView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SecondPageModel}">
<v:SecondPageView />
</DataTemplate>
</TabControl.Resources>
</TabControl>
I'm working on a WinRT application where i have a Listview with a ComboBox.
The Listview has a particular ObservableCollection as Itemssource, The ComboBox Should have another ObservableCollection as ItemsSource because i should be able to dynamicaly change the contents of the ComboBox.
I'm using the MVVM-Light framework, The ObservableCollections are filled in the ViewModel and displayed through databinding.
I'll give you an example Xaml code:
<Page x:Class="MvvmLight2.MainPage"
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:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
d:DesignHeight="768"
d:DesignWidth="1366"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Page.Resources>
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{Binding CollectionOne}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding StringOne}"></TextBlock>
<ComboBox ItemsSource="{Binding CollectionTwo}" Width="500">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding StringTwo}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
And Corresponding ViewModel:
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
CollectionOne = new ObservableCollection<ClassOne>();
for (int i = 0; i < 4; i++)
{
var temp = new ClassOne()
{
StringOne = "String " + i.ToString()
};
CollectionOne.Add(temp);
}
CollectionTwo = new ObservableCollection<ClassTwo>();
CollectionTwo.Add(new ClassTwo("ADV"));
CollectionTwo.Add(new ClassTwo("Wettelijk"));
}
private ObservableCollection<ClassOne> _collectionOne;
public ObservableCollection<ClassOne> CollectionOne
{
get { return _collectionOne; }
set
{
if (_collectionOne == value)
{
return;
}
_collectionOne = value;
RaisePropertyChanged(() => CollectionOne);
}
}
private ObservableCollection<ClassTwo> _collectionTwo;
public ObservableCollection<ClassTwo> CollectionTwo
{
get { return _collectionTwo; }
set
{
if (_collectionTwo == value)
{
return;
}
_collectionTwo = value;
RaisePropertyChanged(() => CollectionTwo);
}
}
}
In ClassOne and ClassTwo are for the example just one property in each class with a string.
Both collections have to remain seperate because they can be different in length when randomly filled.
EDIT
#Josh I followed your instructions but it still doesn't seem to work, Here are my adjustments:
<Page x:Class="MvvmLight2.MainPage"
x:Name="MyControl"
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:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
d:DesignHeight="768"
d:DesignWidth="1366"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Page.Resources>
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ListView ItemsSource="{Binding CollectionOne}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding StringOne}"></TextBlock>
<ComboBox ItemsSource="{Binding ElementName=MyControl, Path=CollectionTwo}" Width="500">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding StringTwo}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Since you are using the ViewModel Locator to set your datacontext you can reuse this to find the property CollectionTwo.
Your binding would look like this:
<ComboBox ItemsSource="{Binding Path=Main.CollectionTwo, Source={StaticResource Locator}}" />
You need to move up one level in the datacontext to search the view model instead of the item that is bound at the list view level using RelativeSource:
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Page}}, Path=CollectionTwo}" />
and for WinRT situations, use a control name:
ElementName=MyControl
instead of searching by AncestorType and give the page a name of 'MyControl'. It would then look like
<ComboBox ItemsSource="{Binding ElementName=MyControl, Path=DataContext.CollectionTwo}" />
and your Page would look like
<Page x:Name="MyControl"
The ComboBox binding is relative to the Binding of the ListItem. So it searches for CollectionTwo as a property of ClassOne. Either look at the RelativeSource to bind to or move the CollectionTwo to class ClassOne. That way, you can easily build up different lists for each ListViewItem.
I have the following code that should display some information about ContactLists in a ListBox but there seems to be a problem with the binding as nothing is displayed. What am I missing? Would appreciate any help. Thanks!
XAML
</Window>
<Window.Resources>
<DataTemplate x:Key="ContactsTemplate">
<WrapPanel>
<TextBlock TextWrapping="Wrap"
Text="{Binding ContactListName, Mode=Default}"/>
</WrapPanel>
</DataTemplate>
</Window.Resources>
<Grid x:Name="LayoutRoot"
Background="#FFCBD5E6">
<Grid.DataContext>
<local:MyViewModel/>
</Grid.DataContext>
<ListBox x:Name="contactsList"
SelectionMode="Extended"
Margin="7,8,0,35"
ItemsSource="{Binding ContactLists}"
ItemTemplate="{DynamicResource ContactsTemplate}"
HorizontalAlignment="Left"
Width="178"
SelectionChanged="contactsList_SelectionChanged"/>
</Grid>
</Window>
ViewModel
public class MyViewModel
{
public ObservableCollection<ContactListModel> ContactLists;
public MyViewModel()
{
var data = new ContactListDataAccess();
ContactLists = data.GetContacts();
}
}
Change ContactLists to be a property for the binding to work correctly:
public class MyViewModel
{
public ObservableCollection<ContactListModel> ContactLists{get;set;}
public MyViewModel()
{
var data = new ContactListDataAccess();
ContactLists = data.GetContacts();
}
}
See here for more info.