I have a working program in Caliburn Micro but am moving over to MvvmCross. What I have working in Caliburn is a ShellView (Parent) that displays my navigation buttons and a cart. On that view, there is another view which is my selection of the navigation buttons, let's call it ActiveView (the view changes, in Caliburn it was ActiveItem() to change the view).
In MvvmCross, I cannot get this same functionality to work. After 3 days of searching and reading, I need help. Here is the image of the program, Blue outline is ShellView, inside of it Red outline is ActiveView.
What I get with MvvmCross is the ShellView, with no ActiveView. So the parent works, but no child is displayed. I have created a few other MvvmCross apps but they contain no navigation.
I have 2 Code Versions, First works but creates a second Window. Second keeps a single Window, but does not navigate. I need a single window with navigation. I feel I have a core misunderstanding and cannot find a source that explains it.
First Sample Works, but creates 2 Windows. An empty window (from MainWindow.xaml) and a second from ShellView. My assumption for 2 Windows opening when app is ran, is MainWindow being a window, and ShellView also being set to Window in xaml and cs.
Based on MvvmCross Playground.Wpf
<views:MvxWindow
x:Class="MvxKioskMtg.Wpf.Views.ShellView"
xmlns:views="clr-namespace:MvvmCross.Platforms.Wpf.Views;assembly=MvvmCross.Platforms.Wpf"
xmlns:mvx="clr-namespace:MvvmCross.Platforms.Wpf.Binding;assembly=MvvmCross.Platforms.Wpf"
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:MvxKioskMtg.Wpf.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Background="#3d3d3d"
>
<views:MvxWindow.ContentTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Content="Welcome" Command="{Binding ShowWindowChildCommand1}" Grid.Column="0"/>
<Button Content="Checkout" Command="{Binding ShowWindowChildCommand2}" Grid.Column="1"/>
</Grid>
<ContentPresenter Content="{Binding}" Grid.Row="1" />
</Grid>
</DataTemplate>
</views:MvxWindow.ContentTemplate>
</views:MvxWindow>
ShellView.xaml.cs
public partial class ShellView : MvxWindow
{
public ShellView()
{
InitializeComponent();
}
}
Second Version Layout looks correct, but no navigation. Click buttons has no effect. Adding break point to the Command that changes views and it is never reached.
XAML changed from MvxWindow to MvxWpfView in 4 places, otherwise same as First Version.
<views:MvxWpfView
<views:MvxWpfView.ContentTemplate>
</views:MvxWpfView.ContentTemplate>
</views:MvxWpfView>
ShellView.xaml.cs
public partial class ShellView : MvxWpfView
{
public ShellView()
{
InitializeComponent();
}
}
In the XAML, if I update the DataContect to AncestorType={x:Type views:MvxWpfView} from Window, it does update the View, but the entire View. Not the ContentPresenter space. So I lose my navigation buttons.
Which of these methods is correct, and what am I doing incorrect? Am I totally off base? Thank you for any help and guidance you can provide. I'll happily read any sources.
Related
This question already has answers here:
What is DataContext for?
(4 answers)
Closed 4 months ago.
sorry about this question. I know MVVM exist for many years but each time I try to code something with it I face the same issue again and again ans I'm still looking for a real good tutorial about this.
Let's consider we have a main window (MainWindow.xaml) with its view model (MainViewModel.cs).
This window has a grid, in my grid I define 2 user controls. Whatever it is. One is on the left, one on the right. On my main window I have create, in MainViewModel.cs an engine:
internal class MainWindowViewModel
{
public MainWindowViewModel()
{
QCEngine qcEngine = new();
}
}
This engine is my unique model and contains a complex code that read data. Whatever. This engine has a public list of value. I want to display these values on my left and right panels in different ways. Again whatever. The display is not my issue.
My issue is how I pass this list or the entire engine reference to my panels? I'm really lost. I can do this in few seconds with any classic WinForms but I never figure out how to do in MVVM. I'm at this moment where I give up MVVM to do classic WinForms. This time I want to understand.
Can you help me?
My QC engine is a RFID reader. It already works fine as console application. All parameters are in a config file. the idea of the interface is to give more flexibility to the reader. Having a nice result screen, a setting screen, some interactions.
<Window x:Class="Beper.QCTable.Control.View.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:view="clr-namespace:Beper.QCTable.Control.View"
xmlns:viewmodel="clr-namespace:Beper.QCTable.Control.ViewModel"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Window.DataContext>
<viewmodel:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Menu -->
<Menu Grid.Row="0" FontSize="20">
<MenuItem Header="_Menu">
<MenuItem Header="_Advanced"/>
</MenuItem>
</Menu>
<!--Header-->
<StackPanel Grid.Row="1" Background="Orange">
<TextBlock FontSize="20">
Header
</TextBlock>
</StackPanel>
<!--Body-->
<Grid Grid.Row="2">
<view:TabPanel/>
</Grid>
<!--Status Bar-->
<StatusBar Grid.Row="3" FontSize="20">
<StatusBarItem>
Status
</StatusBarItem>
</StatusBar>
</Grid>
</Window>
Focus on tab panel:
public class TabPanelViewModel
{
public ObservableCollection<TabItem> Tabs { get; set; } = new ObservableCollection<TabItem>();
public TabPanelViewModel()
{
Tabs.Add(new TabItem { Header = "One", Content = "One's content" });
Tabs.Add(new TabItem { Header = "Two", Content = "Two's content" });
}
}
I cannot chare the engine code but, really, it is just a list of keys (RFID keys / EPC). This is the only public data. I want to display this list of key by group under my tabs.
Passing "this list or the entire engine reference" to the view defats the purpose of implementing the MVVM design pattern in the first place.
What you should do is to use the engine to prepare and set the state of your app/view in your view model.
The controls in the views should then bind to properties of the view model that contains, and effetively defines, the current state.
I am trying to change the size of a grid using data binding in order to be able to expand certain sections of my UI. I have looked online and nothing really seems to work. My window has a frame that loads a "root page" with another frame inside of it as seen in rootpage.xaml below. I have added a textbox but that does not seem to work. Interestingly enough, when I click the button present on the page. I do get a line outputted so my code is running. Adding a breakpoint shows similar results that the property FullScreenValue does in fact change. I think I am missing something in order to get the PropertyChanged event to be received or I may be unintentionally instancing multiple instances of my ViewModel. Looking for a solution.
My root page loaded into the frame in my window:
<Page
x:Class="MVVM.Views.rootPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVM.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:ViewModels="using:MVVM.ViewModels"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext>
<ViewModels:RootPageViewModel x:Name="ViewModel"/>
</Page.DataContext>
<Grid x:Name="frameGrid" ColumnDefinitions="*, *" RowDefinitions ="*, *" Grid.Row="1" Margin="15, 0, 15, 0" >
<Frame x:Name="topleftFrame" Grid.Column="0" Grid.Row="0" CornerRadius="8" Margin="10, 10, 10, 10" Grid.ColumnSpan="{Binding FullScreenValue,Mode=OneWay}" Grid.RowSpan="{Binding FullScreenValue, Mode=OneWay}"/>
<AppBarButton Icon="FullScreen" Grid.Column="0" Grid.Row="0" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="10,5,5,10" Command="{Binding FullScreenCommand, Mode=OneWay}"/>
Visibility="Visible"/>
<TextBlock Text="{Binding FullScreenValue, Mode=OneWay}" Grid.Row="1" Grid.Column="1"/>
</Grid>
</Page>
My ViewModel:
namespace MVVM.ViewModels
{
public partial class RootPageViewModel : ObservableObject
{
[ObservableProperty]
private int fullScreenValue;
public RootPageViewModel()
{
fullScreenValue = 1;
}
[ICommand]
void FullScreen()
{
fullScreenValue = 2;
Debug.WriteLine("HI");
}
}
}
As you can see I am trying to make use of the community toolkit. I have tried replacing some of these generated snippets with standard mvvm properties and commands to no avail. If I change the constructor to have a value of 2 I get my desired result, but can't seem to do it with this button! Send help.
My desired result is upon clicking the button, the frame will now span 2 columns and 2 rows instead of 1 column and 1 row. Maybe because this sort of UI concerns strictly the view it does not necessarily need a ViewModel but nonetheless I am still looking for help as I feel like I will run into this problem in the future. I am new to a lot of things in the Microsoft ecosystem so please forgive me if this is a repeat question and I just could not understand other answers.
I am trying to accomplish a navigation bar and a content screen in .NetFramework WPF application. My goal is to implement MVVM pattern. The main objective is when I hit Users button in navigation bar, I want my Body page to render "Users" text. And when I press Actions button, body page must render "Actions" text. Navigation buttons are "Users" and "Actions". I have 3 View pages that are OperationPage, Navbar and Body. OperationPage is using Navbar and Body XAML to create view. The grid code is as following :
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20*" />
<RowDefinition Height="80*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<local:Navbar/>
</Grid>
<Grid Grid.Row="1">
<local:Body/>
</Grid>
</Grid>
Navbar grid is as follows :
<UserControl.DataContext>
<vm:NavbarViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50*" />
<RowDefinition Height="25*" />
<RowDefinition Height="25*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Text="{Binding Path=CurrentPage}" FontSize="30"/>
</Grid>
<Grid Grid.Row="1">
<Button Content="Users" Width="75"/>
</Grid>
<Grid Grid.Row="2">
<Button Content="Actions" Width="75"/>
</Grid>
</Grid>
And Finally Body grid is as following :
<Grid>
<TextBlock Text="{Binding Path=CurrentPage}" FontSize="30"></TextBlock>
</Grid>
I have created a ViewModel classes for both Navigation and Body that implements INotifyPropertyChanged. I can change the text from these classes. One of them is as following for navigation bar.
class NavbarViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyChange(string changedVar) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(changedVar));
private string _currentPage = "Navbar Data";
public string CurrentPage
{
get { return _currentPage; }
set
{
_currentPage = value;
NotifyChange("CurrentPage");
}
}
}
But I am stuck with connecting these two classes to a Singleton ViewState class. Should I again implement two-way binding with INotifyPropertyChanged to my model class or Should I follow another way? I tried implementing the INotifyPropertyChanged class again in my Model class but I could'not find a way to create connection between the ViewModel class and model.
You might solve this in a couple of ways.
The first way is to strongly-couple your ViewModels, like this.
OperationPage View
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20*" />
<RowDefinition Height="80*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<local:Navbar DataContext="{Binding NavbarViewModel}"/>
</Grid>
<Grid Grid.Row="1">
<local:Body DataContext="{Binding BodyViewModel}"/>
</Grid>
</Grid>
Then implements the OperationPageViewModel. This ViewModel should implement the INotifyPropertyChanged interface and it must have the 2 properties named NavbarViewModel and BodyViewModel of types NavbarViewModel and BodyViewModel respectively.
You might need to add to NavbarViewModel 2 events binded to Users' button clicked and Groups' button clicked in order to expose them outside and OperationPageViewModel should monitor those events (it has the instance of NavbarViewModel) then for example set CurrentPage property of BodyViewModel accordingly on user's button click.
Another solution might be to decouple ViewModels.
This solution get you better code maintenance and also let you code less than the first solution.
You need to use Message Broker design pattern https://en.wikipedia.org/wiki/Message_broker
you can implement your own Message Broker or simply use one of any MVVM Toolkit libraries that already implement it.
Most of the existing MVVM Toolkits have their own Message Broker implementation.
So no need to re-invent the whell.
For example, the MVVM Light Toolkit Message Broker is explained here: https://learn.microsoft.com/en-us/archive/msdn-magazine/2014/june/mvvm-the-mvvm-light-messenger-in-depth#using-messages
I am trying to make a program in winRT that uses the MVVMlight framework. In the application i have a part that should remain constant and a part that should have its content linked to a specfic viewmodel. I will give an little example below of what i am trying to say:
So when i press the Grey button the content should be grey and when i press the red button the content should be red, however the rest of the page should remain constant.
The only way i could think of now is to put multiple datatemplates in my view and only fill the list to which they are bound when i need them caussing them to appear when filled and dissapear when cleared, but i think this will make the view a bit of a mess and i want to know if there arent any other ways to do this?
What i would really like to achieve is when i click a button (grey or red) that there will be a view with a correspoding viewmodel that will be loaded into the contentarea, the contentarea beying the square that is colored red/grey atm.
It should be something like i found in this tutorial but for WinRt cause i cant get this tutorial to work in WinRt.
http://www.codeproject.com/Articles/323187/MVVMLight-Using-Two-Views
Try something like this, a wpf window with a content control that binds to a usercontrol property on the view model:
<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"
mc:Ignorable="d"
x:Class="MainWindow"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
DataContext="{Binding Main_VM, Source={StaticResource Locator}}"
Background="#FF1D1D1D"
WindowState="Maximized"
WindowStyle="None"
WindowStartupLocation="CenterScreen" ResizeMode="CanResizeWithGrip"
MinHeight="750" MinWidth="1050">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="700" MinWidth="1000">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<ContentControl Name="UC_Main" Content="{Binding UC_Main}" Grid.Column="1" Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<!--workspace user control goes here-->
</ContentControl>
</Grid>
</Window>
you can some buttons, or listview etc that changes the value of the usercontrol property. the following is the viewmodel of hte view:
Public Class MainWindowViewModel
Inherits ViewModelBase
#Region "DECLARATIONS"
Public Const CC_Main As String = "UC_Main"
Private _ucMain As UserControl = Nothing
#End Region
#Region "PROPERTIES"
Public Property UC_Main() As UserControl
Get
Return _ucMain
End Get
Set(value As UserControl)
If _ucMain Is value Then
Return
End If
RaisePropertyChanging(CC_Main)
_ucMain = value
RaisePropertyChanged(CC_Main)
End Set
End Property
#End Region
#Region "COMMANDS"
#End Region
#Region "CONSTRUCTOR"
Public Sub New()
UC_Main = New YourUserControl
End Sub
#End Region
#Region "METHODS"
#End Region
End Class
obviously these have both been simplified but should show you what is possible. YourUserCOntrol is the view you want to be displayed in the content control of the main window. Then you can use the mvvm-light relay command on a button or event to change/set the usercontrol to a new one. You can have as many content controls on your page as you need.
How to make a button let a picture show up ?
what i Need to do, is creating 300 buttons each of these buttons will represent a unique path and pic every pic is defferent than the other? Unfortunately, WPF SketchFlow or Silverlight SketchFlow (I don't know the difference) can not be running as a program on computer. so what left is WFP app. Furthermore, i find it so difficult to just create one button linked to a pic's path!!!!.
I watched all of the Microsoft training Videos(the 5 days ones, if u dont know what i mean)http://www.microsoft.com/expression/resources/blendtraining/ . And still cant do it. I was searching for almost 4 weeks, and i still do.
Any idea how to create this essay app which require lots of work.
summery: 1-I don't know which app to use Silverlight or WPF app?(I don't know the difference)
2-i need to create a button that when i press on it, (New sorry) there will be a pic showing up in certain place that ill choose).
so in the end i need the code that will let the button work. direction for further info that ill need(websites that include vids or training by any type)
My regards SHeeDeED :)
Maybe you should start with a simple program: 1 button, showing 1 picture.
Handcraft it in XAML (no blend) and when you're stuck post a specific question here with the code.
I use Blend for most of my XAML/WPF UI design, and it works pretty well for me. I did, however, learn WPF and XAML by writing it longhand in the Visual Studio designer first. For any coding other than XAML (VB.NET, C#, C++, etc) I would highly suggest using Visual Studio as there are no bells and whistles in the Blend code window. It is essentially a colorful text editor.
As for buttons displaying images I have a couple of questions. I imagine your buttons are going to be loaded from a collection of some objects or something. By far, this is better than specifying each button in the XAML code.
I will assume your collection of objects has an ImagePath (or similarly named) property. The following should work out alright.
Load your ListBox with the Items, (see either http://www.WindowsClient.net for the old [Windows Forms] way, or read up on MVVM for the newer better way). Below is my listbox; ItemsSource is bound to the ImageCollection property on my ViewModel in this case. The items in my Imagecollection have an ImagePath property that is just a string file path to the image.
<ListBox ItemTemplate="{DynamicResource MyImageButton}" ItemsSource="{Binding ImageCollection}"/>
In Blend, you can create a DataTemplate by right clicking on the listBox, and going to Edit Additional Templates > Edit Generated Items (ItemTemplate).
From there you are just editing the layout of the items that will be created in the listbox. Below is my example of an item button with an image bound to the ImagePath property.
<DataTemplate x:Key="MyImageButton">
<Button Width="75">
<Button.Content>
<StackPanel>
<Image Source="{Binding ImagePath}" HorizontalAlignment="Left" Height="64" Width="64"/>
</StackPanel>
</Button.Content>
</Button>
</DataTemplate>
Let me know if you need more info and I can post some more resource links.
EDIT
Alright, so here is a simple little application that I made to illustrate how this can be done probably the easiest as possible. In this application, I have 3 classes:
ImageButton - It just consists of two string properties, ImagePath and ImageName.
ImageButtonCollection - Inherited from ObservableCollection, creates and adds 300 buttons (iterates 1 to 300 and sets ImagePath to "C:\Images\image{i}.png" and ImageName to "image{i}".
The MainWindow class - I will post the contents below.
MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ImageButtons"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource x:Key="ImageButtonsCVS"/>
</Window.Resources>
<Grid>
<ListBox Height="311" HorizontalAlignment="Left" x:Name="ListBox1" VerticalAlignment="Top" Width="268" HorizontalContentAlignment="Stretch">
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:ImageButton}">
<Border Background="#5A000000" CornerRadius="5">
<Grid Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.ColumnSpan="1" Height="30" Width="30" BorderBrush="#38FFFFFF" BorderThickness="1" Margin="5" Padding="0">
<Image Source="{Binding ImagePath}"/>
</Button>
<TextBlock Margin="0" TextWrapping="Wrap" Text="{Binding ImageName}" d:LayoutOverrides="Width, Height" Grid.Column="1" VerticalAlignment="Center" Foreground="White"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.Resources>
<ListBox.ItemsSource>
<Binding Source="{StaticResource ImageButtonsCVS}"/>
</ListBox.ItemsSource>
</ListBox>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="322,54,0,0" x:Name="Button1" VerticalAlignment="Top" Width="75" Click="CreateButtons" />
</Grid>
MainWindow.xaml.vb - I am more familiar with VB, but this can be easily ported to C# or whatever.
Class MainWindow
Private Sub CreateButtons(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim ImageButtonCVS As CollectionViewSource = Me.FindResource("ImageButtonsCVS")
ImageButtonCVS.Source = New ImageButtonCollection
End Sub
End Class
So, the ListBoxes ItemsSource property is bound to the CollectionViewSource created in the Window.Resources. The ListBox also has in it's resources a DataTemplate for the ImageButton class. Anytime an ImageButton is displayed in that listbox, that template will be used. The buttons click event is set to the CreateButtons method in the codebehind.
That method finds the CollectionViewSource resource, and sets it's source to a new instance of the ImageButtonCollection class. Once that is done, the UI is notified of an update (since the ObservableCollection has built in UI notification) and changes accordingly displaying 300 buttons with a little textblock next to them with the image name.
If you run this, create a folder on your C drive with some images named "imageX.png" with x being a number 1 to 300. Below are the other two classes so you can create/compile/run it.
ImageButton class
Public Class ImageButton
Public Property ImagePath As String
Public Property ImageName As String
Public Sub New()
End Sub
Public Sub New(ByVal Path As String, ByVal Name As String)
Me.ImagePath = Path
Me.ImageName = Name
End Sub
End Class
ImageButtonCollection class
Imports System.Collections.ObjectModel
Public Class ImageButtonCollection
Inherits ObservableCollection(Of ImageButton)
Public Sub New()
For i As Integer = 1 To 300
Me.Add(New ImageButton(String.Format("C:\Images\image{0}.png", i), String.Format("Image{0}", i)))
Next
End Sub
End Class
You never specified how you wanted the image displayed. You may not want them to be in a listbox. The beauty of WPF is that once you get it working in a listBox or whatever ItemsControl, you can easily change to another ItemsControl, or even customize one. Go into the ListBox template and change the ItemsHost to use a uniform grid or to stack items horizontally instead of vertically.