I'm code some app with different pages in one window. First of all i use MVVM pattern, but later i understand what using this pattern for so small application is not necessary.
But im MVVM version i use page converter for xaml binding.
Let me show what i mean:
MainWindow.xaml
<Frame
Name="MainFrame"
Content="{Binding ApplicationViewModel.CurrentPage, Source={x:Static local:ViewModelLocator.Instance}, Converter={conv:ApplicationPageValueConverter}}"
NavigationUIVisibility="Hidden" />
In ValueConverter i use switch with enum parameter, and return new Page(),
like:
case ApplicationPage.HelloPage:
return new HelloPage();
So i regreet MVVM pattern.
But now then i use this container, it doesnt work at all.
I use backend file for pages and window. In window backend file i use property with OnPropertyChanged. But when page changes, converter dont work.
Ok, i found answer.
I forgot specify DataContext of backend file.
public MainWindow()
{
InitializeComponent();
Thread.CurrentThread.CurrentCulture = new CultureInfo("ru-RU");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("ru-RU");
DataContext = this;
}
So, that's it! =)
Related
I’m working on a Xamarin Forms project, where I have a screen divided into 2 parts, inside a single View (say DashBoardView.Xaml). One part is having some menu options and on the other part I want to load different views on menu option clicked.
The dynamically views having different ViewModels registered (using XLABS MVVM) ViewFactory.
DashBoardView.Xaml
<Grid>
<Grid ColumnSpacing="0" RowSpacing="0" BackgroundColor="#404040">
// menu options having Command binded in DashboardViewModel
</Grid>
<ContentView Grid.Row="0" Grid.Column="1" BackgroundColor="White"
Content="{Binding DashboardDetailView}">
// views are binded with DashboardDetailView
// views are loaded dynamically to DashboardDetailView
(bindable property) using new Keyword when menu option
command is executed.
</ ContentView>
</Grid>
DashBoardViewModel.CS
private Command _dashboardMenuOptionCommand;
public Command DashboardMenuOptionCommand
{
get
{
return _ dashboardMenuOptionCommand?? (_dashboardMenuOptionCommand = new Command(() => {
DashboardDetailView = new ABCView();
}));
}
}
Note: ABCView is a Xaml view registered with ABCViewModel using xLab’s ViewFactory
Problem: The issue is the ABCView is getting loaded but no user interaction is happening, as in no commands binded to a label in ABCViewModel is getting called. Is there something I’m doing wrong or is there any better or optimize way to achieve the same?
Please find the attached screenshot for the reference.
DashboardView.xaml
Instantiating a view won't automatically instantiate a viewmodel and set it as it's binding context.
Most (if not all) MVVM Frameworks will provide you with a method to instantiate or resolve a page, which will automatically look up and hook up the appropriate viewmodel as well. Not too sure about automatically resolving a view with a viewmodel.
If not supported by your framework, you can do it yourself by instantiating your viewmodel, then assigning it to the view's binding context, before adding the view to the page.
Please note that XLabs is a dead project, I'm not too sure it's a smart idea to use it going forward, I suggest you look into current MVVM frameworks (there's a bunch) and pick one that suits your needs and looks good to you.
I am new to WPF and the MVVM pattern and a little confused on how to achieve data-binding to pre-existing instances. I understand that in MVVM you have the Model="business logic", ViewModel="presentation logic", View="actual presentation".
Unfortunately I am currently unable to figure out how you would go about binding your View to existing instances of the ViewModel and corresponding Model. I have found for example this MVVM tutorial, which I liked, but it also just creates the Model inside the ViewModel.
So how would I go about using previously instantiated Models from my underlying application?
EDIT: So I am getting around to trying out the method proposed by Gusdor in a small test project, but cannot get it to work. I am getting an error 'WpfBindingTesting.App.MyViewModel' is a 'field', but used like a 'type' in App.xaml.cs (see below). I hope somebody catches what I am doing wrong. Here is my code:
ViewModel:
namespace WpfBindingTesting
{
class ViewModel
{
private List<string> names;
public List<string> Names
{
get { return names; }
set { names = value; }
}
public ViewModel()
{
Names = new List<string> { "string1", "string2", "string3" };
}
}
}
MainWindow.xaml.cs:
namespace WpfBindingTesting
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
MainWindow.xaml:
<Window x:Class="WpfBindingTesting.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
DataContext = "{Binding RelativeSource={RelativeSource Mode=Self}}"
>
<StackPanel>
<ListBox
ItemsSource="{Binding Path=Names}"
Height="50"
>
</ListBox>
</StackPanel>
</Window>
App.xaml.cs:
namespace WpfBindingTesting
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App(){
MainWindow View = new MainWindow();
ViewModel MyViewModel = new ViewModel();
View.DataContext = MyViewModel; // this give error: 'WpfBindingTesting.App.MyViewModel' is a 'field', but used like a 'type'
//view.DataContext = new ViewModel();
//view.Show();
}
}
}
Generally speaking, we assign view models to the view's DataContext property.
You can do this from code as long as you have a reference to the view. Here is the code:
C Sharp
Window view = new Window();
// add a view model
view.DataContext = new ViewModel() { Title = "View Model 1" };
view.Show();
// add another view model to demonstrate the binding
view.DataContext = new ViewModel() { Title = "View Model 2" };
XAML
The XAML for window might look something like this.
<Window Title={Binding Title}>
</Window>
Notice how the Source or ElementName properties remains unspecified in the binding declaration? This instructs the Binding to use the current DataContext as the source.
Model = business.
View = what the user see, and really ain't about logic (this is a lie ofcourse, but this is what you strive for).
ViewModel = what glues them together. It's your adapter that gives the View the info it needs from the Model you have.
I suggest having a look at an MVVM platform (like MVVM Light), which gives you lot of the stuff you need to do "for free" when you begin.
As a guide, here are 3 options for doing your binding:
1.1 On the XAML, set DataContext = "{Binding relativeSource={RelativeSource self}}
1.2 have a property on the code behind (your xaml.cs)
1.3 Initialize the property BEFORE the InitializeComponent() in the constructor.
2.1 Igonre DataContext on the XAML
2.2 In the code behind, start with InitializeComponent();
2.3 have a line like DataContext = this; in the constructor
2.4 Initialize your properties
3.1 Give the window or UserControl a name: Name = "MyWindow" (in the xaml)
3.2 Bind to an element: ItemSource={Binding ElementName = MyWindow, path =... }"
3.3 Initialize the property BEFORE the InitializeComponent() in the constructor.
And last but not least, have a look at my article "The Big MVVM Template", which you can follow and will give you a good handle of the basic, plus you'll have a running fully documented example on top :) .
Your question is more about design then coding. I think if you have existing instances of viewmodels already and you just want to reuse/reassign them in new views. then probably you need to have a look at IOC Containers( as actually you want some sort of dependency injection here)
To be very simple use an IOC container to maintain the lifetime of your viewModel.
Configure the container to give you the required ViewModel according to your need i.e a new object or an existing view model object.
Unity Container
In unity Container you can just register your objects and resolve them at future as you need them. using below syntax simplay:
Register:
1.MyUnityConatiner.RegisterInstance<IMyService>(myDataService);
or
2.MyContainer.RegisterInstance<IMyService>("Email", myEmailService);
Resolve :
1.MyUnityConatiner.Resolve<IMyService>();
or
2.MyConatiner.Resolve<IMyService>("Email");
I know this will seem to you a little deviated solution but if you learn to use this you will have a different perspective on your problem and how to reuse the objects(model/viewmodel).
I want to create textblock programmatically
But it seems the code behind file don't build it.
EDIT
Inside Main.cs
public Main()
{
InitializeComponent();
}
public void generateUI(TypeOne item)
{
// Create first element
TextBlock authorText = new TextBlock();
authorText.Text = "Saturday Morning";
authorText.FontSize = 12;
authorText.FontWeight = FontWeights.Bold;
gridUI.Children.Add(authorText);
}
Inside Main.xml
<Page.DataContext>
<ViewModels:MainWindowViewModel/>
</Page.DataContext>
<Grid Width="Auto" Background="WhiteSmoke" x:Name="grid">
<Grid x:Name="gridUI" Margin="0,68,0,-37">
</Grid>
</Grid>
Inside MainWindowViewModel.cs
Main genUI = new Main();
IEnumerable<TypeOne> generateUI = //query variable
from x in _txnType
where x.Description == selectedTypeOne
select x;
foreach (TypeOne ui in generateUI)
{
genUI.generateUI(ui);
}
But the public void generateUI(TypeOne item) not creating the textblock.
I supposed, the Main.xml cannot read it since the DataContext is set to MainViewModel.cs
Please help.
The fact that you want to follow the MVVM pattern doesn't exclude dynamic view creation. However I always recommend that you use "logic-only" in the view model. That means that the VM still only contains logic for the view to interact with, but has absolutely no knowledge about the view and how it behaves.
Your view is the one using the view model and should adapt the view dynamically. This means that in the code-behind of your view, you have access to your view model and can customize the view according to your current view model. You can respond to changes in the view model as well since the view model will implement INotifyPropertyChanged (which you can intercept in your code-behind just as normal bindings would).
The question you should ask yourself is whether MVVM is the right patter for you here. If the UI always comes from a database, then where are the bindings defined? Also in the database? If so, what actual logic are you implementing in the view model? In other words: what is the point of a VM if there is no custom logic. In that case I recommend to use the view-only approach. If you need custom logic, use the approach with the code-behind I described above.
Say I have a WPF application (exe) that has this in the MainWindow.xaml:
<Grid>
<extraControls:MyMVVMUserControl MyDependencyProperty="{Binding Something}"/>
<extraControls:MyUserControl MyDependencyProperty="{Binding Something}" />
</Grid>
and my MainWindow.xaml.cs looks like this:
public MainWindow()
{
DataContext = new MainWindowVM();
InitializeComponent();
}
And my MainWindowVM.cs has a property setup for Something that notifies on property changed.
The user controls are made in a separate dll. As you may guess, MyMVVMUserControl has the DataContext set to a view model.
public MyMVVMUserControl()
{
DataContext = new MyMVVMUserControlVM();
InitializeComponent();
}
MyUserControl does not have a DataContext set in the code behind.
So the interesting thing is that they both have MyDependencyProperty setup exactly the same.
But the MVVM version does not work.
After digging in a bit, I found that the {Binding Something} in MainWindow.xaml is using the View Model setup for the MyMVVMUserControl as the DataContext (rather than the DataContext set in MainWindow.cs (set to MainWindowVM)).
And my question is why?
Why would WPF look inside the user control and use it's DataContext for a binding that is in the actual application?
(NOTE: I know I can get around this by setting the source in the binding, but I want others to be able to use my user controls. But with this issue, I now have a built-in "gotcha" for anyone I want to use my user controls.)
I think I understand you problem, and I'm gonna to give a solution that works for me (I had this problem before). The think is that seams that you are setting the DataContext for you MyMVVMUserControl in code behind, and then it take the bindings from that.
The solution I found for this, is to set the datacontext in code behind, but not at the user control. Set the datacontext for the UserControl's child item. For instance, supose this is the Xaml of your UserControl:
<UserControl ... x:Name="userControl">
<Grid x:Name="rootContainer">
...
</Grid>
</UserControl>
Then in the code behind set the rootContainer's data context, in this way all visual children can access to the control data context, and also the user control datacontext is empty.
...
rootContainer.DataContext = new UserControlViewModel();
...
Hope this may helps you to solve your issues...
You really shouldn't ever set the DataContext of a UserControl from inside the UserControl. By doing so, you are preventing any other DataContext from getting passed to the UserControl, which defeats one of WPF's biggest advantages of having separate UI and data layers.
WPF objects only inherit their DataContext from the parent object if the DataContext is not set to anything else. When your MyMVVMUserControl is being created, you are setting the DataContext to a new MyMVVMUserControlVM, which prevents the DataContext from being inherited from MainWindow.
So its normal that your MVVMUserControl would have it's DataContext set to your MyMVVMUserControlVM, because you set it explicitly in the UserControl's constructor.
This is by-design. UI objects in WPF/MVVM are only meant to be visual representations of the data layer, so it wouldn't make much sense to set the data layer and then try to bind your properties to something that is not on the data layer.
For example, take this line of code:
<UserControl DataContext="{Binding ClassA}" Content="{Binding Name}" />
This would bind the Content property to UserControl.DataContext.Name, which is ClassA.Name. It wouldn't make much sense if this would result in binding to UserControl.Parent.DataContext.Name, as the binding should refer to to the current object's DataContext, and not the Parent's DataContext.
So the only time I ever set the DataContext of a UserControl from inside the UserControl itself is if the UserControl is its own separate object that is never meant to interact with data from the rest of the application. Which so far has been never :)
Typically my UserControls are almost always one of two things:
Either a visual representation of a ViewModel (or Model), such as a CustomerUserControl for a CustomerViewModel, in which case I pass them the DataContext they need when they get used
For example,
<local:CustomerUserControl DataContext="{Binding SelectedCustomer}" />
or
<DataTemplate DataType="{x:Type local:CustomerModel}">
<local:CustomerUserControl />
</DataTemplate>
Or a self-sustained UI object that receives any external data it needs via custom DependencyProperties, and does any additional logic in the code-behind the control, such as a DatePicker control that has a SelectedDate dependency property, or a CalculatorUserControl with dependency properties for Equation and Value
<local:DatePickerUserControl SelectedDate="{Binding SomeDate}" />
<local:CalculatorUserControl Equation="{Binding SomeString}"
Value="{Binding SomeDouble}" />
In your case, it sounds like you should be using the first case, and should be passing a ViewModel into your UserControl containing the data it needs.
<extraControls:MyMVVMUserControl DataContext="{Binding MyMVVMUserControlVM}"
MyDependencyProperty="{Binding Something}">
or
<extraControls:MyMVVMUserControl MyDependencyProperty="{Binding Something}">
<extraControls:MyMVVMUserControl.DataContext>
<viewModels:MyMVVMUserControlVM />
</extraControls:MyMVVMUserControl.DataContext>
<extraControls:MyMVVMUserControl />
I am using the same window that serves two purposes. Inside my window, i have a listview that I want to bind to DIFFERENT objects based on the purpose.
Actually its just a window that takes in import files.
So initially I had this.
<ListView Grid.Row="1" Name="_lvValues"
DataContext="{Binding ElementName=_listbox,Path=SelectedItem}"
ItemsSource="{Binding Path=DataTable(from selectedItemObject)}">
For the other purpose I had to do this
<ListView Grid.Row="1" Name="_lvValues"
DataContext="{Binding ElementName=ClassName,Path=Object}"
ItemsSource="{Binding Path=DataTable(from Object)}">
I want to do this in an if/else statement during the initialization of the window (constructor). So...
if (windowType == Type1)
// SetBinding to using listbox
else
// SetBinding to using Object
I tried this After initialize component
binding = new Binding("DataTable");
binding.Source = new Binding("ListBox.SelectedItem");
_lvValues.SetBinding(ListView.ItemsSourceProperty, binding);
But obviously it didn't work and i have no idea how to proceed.
Reason I need this is, the first window type there is a LIST of file, where second window type only has ONE file so it would not be right to show a listbox with just one file.
Thanks and Regards,
Kev
If your Xaml is an accurate description of your binding you just need to translate it into the two resulting bindings; should be something like this for the first case:
Binding contextBinding = new Binding("SelectedItem");
contextBinding.Source = _listbox;
_lvValues.SetBinding(ListView.DataContextProperty, contextBinding);
Binding itemsBinding = new Binding("DataTable");
_lvValues.SetBinding(ListView.ItemsSourceProperty, itemsBinding);
and the second case is probably this:
Binding contextBinding = new Binding("Object");
contextBinding.Source = ClassName;
_lvValues.SetBinding(ListView.DataContextProperty, contextBinding);
Binding itemsBinding = new Binding("DataTable");
_lvValues.SetBinding(ListView.ItemsSourceProperty, itemsBinding);
(Since the ItemsSource-Binding is always the same and just depends on the DataContext you could refactor it to be outside of the if-clause or in the Xaml altogether i think)