DelegateCommand inside Listview with UserControl - c#

Is there any method to use my DelegateCommand inside my ListView with UserControl:
UserControl:
<UserControl
x:Class="App13.UserControls.ItemTemplateControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App13"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DataContext="{d:DesignInstance Type=local:MainViewModel, IsDesignTimeCreatable=True}"
mc:Ignorable="d">
<Grid>
<Button Content="Click" Command="{Binding OpenCommand"/>
</Grid>
</UserControl>
There is no error in my MainViewModel. There is error in Binding.
I can easily use OpenCommand in MainPage xaml using this code:
d:DataContext="{d:DesignInstance Type=local:MainViewModel, IsDesignTimeCreatable=True}"
<Button Content="Click" Command="{Binding OpenCommand"/>
How can I bind OpenCommnad to my UserControl?
Sorry for my English and thanks in advance!
This is my ListView:
<ListView x:Name="peopleListBox">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<UserControls:ItemTemplateControl/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

In the construction of your UserControl do
this.DataContext = new MainViewModel();
d:DataContext is just the Design time DataContext setting which is not applied at runtime.

By the name of it, DesignInstance is meant for design-time and not run-time.
In MVVM there are two approches of setting your ViewModel.
ViewFirst or ViewModelFirst - depending wether you build your app top down or bottom up.
for ViewFirst You can set your DataContext from your xaml :
<UserControl
x:Class="App13.UserControls.ItemTemplateControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App13"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<UserControl.DataContext>
<local:MainViewModel/>
</UserControl.DataContext>
<!-- Rest of your implementation ... -->
</UserControl>
For ViewModelFirst, set it in your code behind (usually done from View's constructor)
this.DataContext = new MainViewModel();
If you want to bind a property from your viewModel to an Item in your ListBox, bind your button inside the UserControl as follows:
<Button Content="Click"
Command="{Binding DataContext.OpenCommand,
RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
/>
Hope this helps

Related

Setting a datacontext via resources

Evidently using "Resources" to set an control's DataContext does not do what I think. I'm trying to stick close to MVVM. The following is an experiment in setting DataContext.
The MainWindow has a TabControl with two tabs, each displaying my pet's name, initally "Sam". Clicking the "ChangeName" button on Tab 1 changes the pet's name (to "Daisy") as expected. It does not change on Tab 2.
The content of Tab 2 is a Page, with its own DataContext, SecondTabViewModel. So I need to adjust the DataContext in the TextBlock in order to get at MyPet's name. This compiles ok, and Intellisense brings up the right things, so somehow within the control is being set. But the pet's name does not change.
Does the "StaticResource" generate instantiate a new copy of MainWindow or something? Can someone help me out? I'd love to know why this doesn't work, and what would work. This strategy for setting local DataContext is supposed to work according to the docs at https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/?view=netdesktop-5.0 but I must be misreading.
To abbreviate I've omitted some of the code (the pet class. But everything seems to be ok there, in I'm able to change the name on the first tab The Pet class implements INotifyPropertyChanged, I'm using the right handler etc.)
MainWindow.xmal
<Window x:Class="WpfApp9.MainWindow"
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:WpfApp9"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<TabControl>
<TabItem Header="First Tab" Height="50">
<StackPanel>
<TextBlock Text="{Binding MyPet.Name}"/>
<Button Content="Change Name"
Command="{Binding ChangePetNameCommand}"/>
</StackPanel>
</TabItem>
<TabItem Header="Second Tab" Height="50">
<Frame Source="SecondTab.xaml"/>
</TabItem>
</TabControl>
</Grid>
</Window>
MainWindowViewModel
public class MainWindowViewModel
{
public Pet MyPet { get; set; }
public ICommand ChangePetNameCommand { get; set; }
public MainWindowViewModel()
{
MyPet = new Pet();
ChangePetNameCommand =
new RelayCommand(ChangePetName, (Object o) => true);
}
public void ChangePetName(object o)
{
MyPet.Name = "Daisy";
}
}
SecondTab.xmal
<Page x:Class="WpfApp9.SecondTab"
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"
xmlns:local="clr-namespace:WpfApp9"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="SecondTab">
<Page.DataContext>
<local:SecondTabViewModel/>
</Page.DataContext>
<Page.Resources>
<local:MainWindowViewModel x:Key="M"/>
</Page.Resources>
<Grid>
<StackPanel>
<TextBlock Text="{Binding Source={StaticResource M},
Path = MyPet.Name}"/>
</StackPanel>
</Grid>
</Page>
SecondTabviewModel
namespace WpfApp9
{
public class SecondTabViewModel
{
public SecondTabViewModel()
{
}
}
}
The lines
<Page.Resources>
<local:MainWindowViewModel x:Key="M"/>
</Page.Resources>
in SecondTab.xaml are creating a second MainWindowViewModel instance.
In other words, SecondTab does not operate on the original MainWindowViewModel.
You would somehow have to pass a reference to the original MainWindowViewModel instance to SecondTabViewModel.
Instead of using a Frame and a Page, SecondTab could perhaps be a UserControl that simply inherits the DataContext from its parent element, and you could pass a view model object like
<TabItem Header="Second Tab" Height="50">
<local:SecondTab DataContext="{Binding SecondTabVM}"/>
</TabItem>
where SecondTabVM is a property of MainWindowViewModel that holds a SecondTabViewModel instance.

MVVM Light UWP view only showing at design time

I am following a beginner tutorial on MVVM Light and UWP. I have a ViewModel with just a string field that is bound to a TextBlock in main view like this:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Name="Title" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="24" Text="{Binding Title}" />
</Grid>
The ViewModelLocator is defined like this in App.xaml:
<Application.Resources>
<vm:ViewModelLocator xmlns:vm="using:MvvmLight.UWP.ViewModels" x:Key="Locator" />
</Application.Resources>
and the ViewModelLocator class looks like this:
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<StartPageViewModel>();
}
public StartPageViewModel StartPageInstance
{
get { return ServiceLocator.Current.GetInstance<StartPageViewModel>(); }
}
In the ViewModel I have this in the constructor:
Title = "Hello world!";
Now, in Design Time, the text appears fine in the designer, but when I run the app, I only get a blank page, and I cannot figure out why?
I think, you only declare the Design Time DataContext, you should also declare the Runtime DataContext in the attributes of your View. For this, add the following:
DataContext="{Binding Source={StaticResource Locator}, Path=StartPageInstance}"
After this, you will have something like that:
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:design="using:App1.Design"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding Source={StaticResource Locator}, Path=StartPageInstance}"
d:DataContext="{d:DesignInstance Type=design:DesignStartPageInstance, IsDesignTimeCreatable=True}"
mc:Ignorable="d">

WPF Navigate from frame in window to Usercontrol and pass parameters to it

I have a modern window in WPF/C# application, in which I added a modern frame:
<mui:ModernWindow x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
WindowStartupLocation="CenterScreen"
Style="{StaticResource EmptyWindow}">
<Window.Resources>
</Window.Resources>
<Grid>
<Menu x:Name="menu" Height="62" VerticalAlignment="Top" >
<MenuItem x:Name="miHome" Header="Home" Click="MenuItem_Home" IsChecked="True" Width="60" FontSize="14" />
<MenuItem x:Name="miClients" Header="Clients" FontSize="14" Click="MenuItem_Clients" Width="65"/>
<MenuItem x:Name="miSuppliers" Header="Suppliers" FontSize="14" Click="MenuItem_Suppliers" Width="81"/>
<MenuItem x:Name="miReports" Header="Reports" FontSize="14" Click="MenuItem_Reporting" Width="71"/>
</Menu>
<mui:ModernFrame Margin="0,75,10,10" x:Name="frame">
</mui:ModernFrame>
</Grid>
I have MenuItems in my application, when I click on Suppliers item, I fill the frame with a usercontrol, like this:
frame.Source = new Uri("/Pages/Suppliers.xaml", UriKind.Relative);
Where Suppliers.xaml design is:
<UserControl
x:Class="MyApp.LinksBar.Suppliers"
xmlns:MyApp="clr-namespace:MyApp"
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"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
mc:Ignorable="d"
d:DesignHeight="575" d:DesignWidth="905">
<UserControl.Resources>
</UserControl.Resources>
<Grid Name="Grid">
<mui:ModernButton x:Name="btnmakePayment" Content="Make Payment" Click="btnMakePayment_Click" Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Top"/>
</Grid>
</UserControl>
When I click on "Make Payment" button, I navigate to another UserControl (MakePayment.xaml):
private void btnMakePayment_Click(object sender, RoutedEventArgs e)
{
NavigationCommands.GoToPage.Execute(new Uri("/Actions/MakePayment.xaml", UriKind.Relative), this);
}
MakePayment.xaml design is:
<UserControl
xmlns:local="clr-namespace:MyApp.Actions" x:Class="MyApp.Actions.MakePayment"
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"
xmlns:mui="http://firstfloorsoftware.com/ModernUI" xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:MyApp="clr-namespace:MyApp"
mc:Ignorable="d"
Loaded="MakePayment_Loaded"
d:DesignHeight="600" d:DesignWidth="866" >
<UserControl.Resources>
</UserControl.Resources>
<Grid DataContext="{StaticResource makePaymentViewSource}" Name="Grid">
<Grid.RowDefinitions>
</Grid.RowDefinitions>
<Label Content="Total" VerticalContentAlignment="Center" HorizontalAlignment="Left" VerticalAlignment="Top"/>
// More design code here ...
</Grid>
</UserControl>
Here comes my question:
I need to pass parameters from Suppliers UserControl to MakePayment UserControl.
How to programmatically pass the parameters in Suppliers and read them in MakePayment?
Thank you.
If you can bind one to the other, that is what you should be doing. By far, the easiest way to make two UserControls "communicate" (or share properties that both can do stuff with) is to define a DependencyProperty on each and bind them two-way.
This way, both always have access to the same value and both can do stuff with it.
Take my own control as an example:
<UserControl x:Class="MyControls.MasterContainerControl"
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"
xmlns:local="clr-namespace:MyControls"
mc:Ignorable="d"
x:Name="masterContainerControl">
<local:ContainerControl SomeProperty="{Binding ElementName=masterContainerControl, Path=SomeProperty}">
<local:ContainerControl.Another>
<local:AnotherControl SomeProperty="{Binding ElementName=masterContainerControl, Path=SomeProperty"/>
</local:ExplorerBase.AddressBar>
<local:ContainerControl.Some>
<local:SomeControl SomeProperty="{Binding ElementName=masterContainerControl, Path=SomeProperty"/>
</local:ContainerControl.Some>
</local:ContainerControl>
</UserControl>
This, of course, all assumes MasterContainerControl, ContainerControl, AnotherControl, and SomeControl all have a DependencyProperty called SomeProperty, and then the bindings seal the deal.
Note: Make sure the default values are defined in MasterContainerControl because those will override the values MasterContainerControl binds to.
If I misunderstood your issue, please let me know.

Master Detail MVVM with Prism and XAML binding view in data context

I have the following dummy application where I'm trying to build a Master Detail with two views. The first is the collection view, which I can successfully select an element of and it displays in the Content Presenter Data Template with the TextBlock and TextBox defined as they are below.
I have tried to Move the TextBlock and TextBox out to a view, but have been unsuccessful at getting it to display the data. If I remove the TBs and uncomment the view, it will display the view but the TBs in the view won't populate.
Of course, the idea is that I will have more than one type.
MainWindow
<Window x:Class="MyApp.Views.MainWindow"
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:views="clr-namespace:MyApp.Views"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"
xmlns:viewModel="clr-namespace:MyApp.ViewModels"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Horizontal">
<views:CollectionView DataContext="{Binding myItemCollection}">
</views:CollectionView>
<ContentPresenter x:Name="Detail" Content="{Binding myItemCollection.SelectedViewModel}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type viewModel:TextViewModel}">
<StackPanel>
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBox Text="{Binding Text}"></TextBox>
<!--<views:TextView/>-->
</StackPanel>
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
</StackPanel>
</Grid>
TextView
<UserControl x:Class="MyApp.Views.TextView"
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"
xmlns:local="clr-namespace:MyApp.Views"
xmlns:viewModel="clr-namespace:MyApp.ViewModels"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<TextBlock Text="Text Item"/>
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBox Text="{Binding Text}"></TextBox>
</StackPanel>
</Grid>
You need to remove the prism:ViewModelLocator.AutoWireViewModel="True" attribute from your TextView.
What it does is it pulls the appropriate view model from the container and assigns the view model to TextView.DataContext. On the other hand in your template you do not explicitly pass the templated data to the TextView control, so it's expected to be inherited via automatic inheritance of DataContext. But that does not work, because TextView.DataContext is explicitly set by prism:ViewModelLocator.AutoWireViewModel="True".
In case it is required to use view model auto wiring you can always set this attribute from referencing scope, that is in XAML in which your view is "used", e.g.:
<StackPanel>
<views:TextView prism:ViewModelLocator.AutoWireViewModel="True" />
</StackPanel>

How to get property from the DataTemplate's parent ViewModel class

I am using MVVVM Light, so I set the DataContext of the Page to the Locator. Then I set the Pivot's ItemSource to a collection property inside "myFirstVM" ViewModel class. But how to set the text of the header of the PivotItem which is in a dataTemplate of TextBox to "MyProperty" which is also defined in "myFirstVM" class?
I look at this example, but cannot figure it out:
How to access Parent's DataContext in Window 8 store apps
Here is my code:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:myApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModel="using:myApp.ViewModel"
x:Class="myApp.MyTripsPage"
mc:Ignorable="d"
DataContext="{Binding Source={StaticResource Locator}}">
<Grid x:Name="LayoutRoot">
<Pivot Name="myPivot"
Tag="{Binding}"
ItemsSource="{Binding myFirstVM.DataSource}"
ItemTemplate="{Binding myFirstVM.ViewDataTemplate}">
<Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding MyProperty, ElementName=myPivot}"/>
</DataTemplate>
</Pivot.HeaderTemplate>
</Pivot>
</Grid>
using ElementName in a binding will bind to the element itself (Pivot in this case), whereas you want to bind to something in the DataContext of Pivot, so just add DataContext to your path:
<TextBlock Text="{Binding DataContext.myFirstVM.MyProperty, ElementName=myPivot}"/>

Categories