I am using PRISM to auto-wire my Views & ViewModels, however I have encountered a problem I cannot solve.
I am using a calendar control, which enables users to create new appointments via opening new modal window & saving it to calendar.
This window, is styled via a ControlTemplate, where I have the following item:
<telerik:RadComboBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Margin="3"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.EmployeeList}">
Items Source of this combobox is the ViewModels DataContext.EmployeeList - ObservableCollection<Employee>.
This would work like a charm as long as it would not be a new pop-out window. That way, I believe it is a userControl as well and therefore my regular code does not recognize any EmployeeList.
There might be 2 ways how to solve it (I don't have direct access to the modal window as it is being automatically generated by the control itself - I am using Telerik suite).
1) Make sure that the ItemsSource will dig deeper than the very first UserControl that it finds. Maybe by slightly changing the code, it will be able to do so? (Maybe using something like AncestorLevel...?).
2) Telerik has shown an example of how to achieve that by the following line:
<local:ViewModel x:Key="ViewModel" /> -- define key first
ItemsSource="{Binding Source={StaticResource ViewModel}, Path=EmployeesSource}"...
BUT the issue with my ViewModel is that under constructor I am passing several interfaces like following:
private readonly IEmployeeRepository _employeeRepository;
public EmployeeView_HolidaysViewModel(IEmployeeRepository employeeRepository)
{
_employeeRepository = employeeRepository;
InitializeCollections();
InitializeCommands();
}
and therefore I can't make the above solution to work at all.
Any help with my problem would be highly appreciated. I simply need to get that list to that modal window's combobox.
In the end I managed to solve the problem by creating additional constructor to my class which looks like following:
public EmployeeView_HolidaysViewModel()
{
_employeeRepository = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IEmployeeRepository>();
InitializeCollections();
}
This way I can easily adopt Solution Nr 2 from the OP.
Related
Good Morning!
I have a WPF application that will display a number of different file types based on command line args it receives. It works fine, but I want to go back and refactor it. I have only been a developer for a few years and would like to master MVVM.
I am using an MVVM design package called Stylet. In my PDF view I am using a Telerik RadPdfViewer control to which Telerik has all this binding stuff built in for you. For example, I am binding the right click context menu with the commands "select all" and "copy" using their pre configured command bindings.
I would like to bind the "Document Source" property TO MY viewmodel so I can pass in the paths of documents I want to load. However, the DataContext of the control is bound to Telerik's CommandDescriptors preventing the binding to my viewmodel.
<telerik:RadPdfViewer x:Name="radPdfViewer" Grid.Row="1"
DataContext="{Binding CommandDescriptors, ElementName=radPdfViewer}"
DocumentSource="{Binding PDFDoc}"
telerik:RadPdfViewerAttachedComponents.RegisterFindDialog="True"
HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch"
telerik:StyleManager.Theme="Office_Black" Grid.ColumnSpan="2">
<telerik:RadContextMenu.ContextMenu>
<telerik:RadContextMenu>
<telerik:RadMenuItem Header="Select All"
Command="{Binding SelectAllCommandDescriptor.Command}" />
<telerik:RadMenuItem Header="Copy"
Command="{Binding CopyCommandDescriptor.Command}" />
</telerik:RadContextMenu>
</telerik:RadContextMenu.ContextMenu>
</telerik:RadPdfViewer>
public class PDFViewModel
{
private string _pdfDoc;
public string PDFDoc
{
get
{
return _pdfDoc;
}
set
{
_pdfDoc = value;
}
}
public PDFViewModel()
{
PDFDoc = #"t:\share\large.pdf";
}
}
I see two choices
I break Telerik's prebuilt command bindings and figure out how to bring the select all and copy functions to my viewmodel.
Stylet has an s:Action function where I can call a method where I can load the document into the RadPdfViewer control using C#. I would need to somehow get control of the gui control in the method of my viewmodel and I am not sure how to do that.
Is there a better way? A little nudge in the right direction would be greatly appreciated.
Jason Tyler's reply got me going in the right direction. Thank you!
So because I am using a ViewModel first pattern, I did not need to specify the DataContext of the user control like I thought...Its already set.
However, his suggestion of binding using the relative source and researching on how to do this (I have never used RelativeSource before..I am kinda new to this stuff) I came across this Stack post
How do I use WPF bindings with RelativeSource?
A Jeff Knight Posted a diagram of how ancestor binding works.
Using that, I was able to figure out the syntax and my document came right up and I can still use the right click context menu items that are bound to Telerik. So now my Xaml looks like this note how the Document source binding has changed.
<telerik:RadPdfViewer x:Name="radPdfViewer" Grid.Row="1"
DataContext="{Binding CommandDescriptors, ElementName=radPdfViewer}"
DocumentSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=DataContext.PDFDoc}"
telerik:RadPdfViewerAttachedComponents.RegisterFindDialog="True"
HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch"
telerik:StyleManager.Theme="Office_Black" Grid.ColumnSpan="2">
<telerik:RadContextMenu.ContextMenu>
<telerik:RadContextMenu>
<telerik:RadMenuItem Header="Select All"
Command="{Binding SelectAllCommandDescriptor.Command}" />
<telerik:RadMenuItem Header="Copy"
Command="{Binding CopyCommandDescriptor.Command}" />
</telerik:RadContextMenu>
</telerik:RadContextMenu.ContextMenu>
</telerik:RadPdfViewer>
I want to use the C# System.Windows.Forms.SendKeys.SendWait() Method to send Keystrokes from an OnScreenKeyboard to a Textbox. Since I may use this OnScreenKeyboard at other places too I created a UserControl with View (for the Design of the Keyboard) and Viewmodel (basically for calling the Sendkeys.SendWait() Method) in an extra project.
Within this project I created a MainView where I included the UserControl via a ContentControl as you can see in the Code below. CurrentPage basically refers to the Viewmodel of the Keyboard.
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:KeyboardViewmodel}">
<view:KeyboardView/>
</DataTemplate>
</Window.Resources>
<Grid>
<Border Background="White">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"/>
</Border>
<TextBox Width="120"/>
</Grid>
I now have the OnScreenKeyboard and a Textbox in my Window. Clicking into the Textbox and pressing buttons of my OnScreenKeyboard will result in text appearing in my Textbox. All Controls within the KeyboardView are set to avoid getting focus. This is necessary to maintain focus on the Textbox.
The Buttons in the KeyboardView all bind to the Command ClickCommandin my KeyboardViewmodel. Here is the code of the KeyboardViewmodel:
public class KeyboardViewmodel : BaseModel
{
public BaseCommand ClickCommand { get; set; }
public KeyboardViewmodel()
{
ClickCommand = new BaseCommand(PressAndRelease);
}
public void PressAndRelease(object key)
{
if (((string)key).Length <= 1)
SendKeys.SendWait((string)key);
else
SendKeys.SendWait("{" + (string)key + "}");
}
}
Now I did create a NuGet Package with these Files and imported them to the project where I want to use my OnScreenKeyboard.
I did do basically the same as when I tested the OnScreenKeyboard before.
But let me explain the structure of the project a little more:
I have a MainView + MainViewmodel. The MainViewmodel manages the navigation between available pages. To show these pages I have - as in the short example before - a ContentControl whose content is bound to a CurrentPage Property. The MainViewis a normal Window, all other Views are UserControls.
In one of these pages I need an OnScreenKeyboard (DetailsView + DetailsViewmodel). So it seemed logical to me to use another ContentControl within the DetailsView:
<Border Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="3" Height="Auto" Width="Auto">
<ContentControl Content="{Binding Path=OnScreenKeyboard}"/>
</Border>
I create the KeyboardViewmodel in the constructor of the DetailsViewmodel. The constructor of the DetailsViewmodel is called in the MainViewmodel at startup.
So now everything works out fine so far, the OnScreenKeyboard is shown on the correct page in the correct place. If I click a button of the OnScreenKeyboard the proper bound command is called and the SendKeys.SendWait() Method is called.
But no text appears in the TextBox. I have a very bad understanding of the SendKeys.SendAwait() Method. Also, the MSDN Documentation seems to be not very exhaustive on this topic.
It states: "Sends the given keys to the active application, and then waits for the messages to be processed."
Now. The Active / Focused Application is my Application. So my guess is that the KeyStrokes should be processed by my Textbox.
My Questions:
Any guesses how to debug the 'SenWait()' Method further e.g. track where the strokes are really sent to or something like that?
Is this the correct way for sending KeyStrokes to an active Application? It seems like SendKeys comes from Windows Forms, I use WPF.
Should I just pass my Textbox as reference to the OnScreenKeyboard and write directly to the referenced Textbox? This would make me much less flexible in regards of reusability.
Update:
As pointed out in the comments this could probably be a duplicate question.
I am well aware of the various different solutions and have already considerd them:
http://wpfkb.codeplex.com/
http://www.codeproject.com/Articles/32568/A-Touch-Screen-Keyboard-Control-in-WPF
http://www.codeproject.com/Articles/145579/A-Software-Virtual-Keyboard-for-Your-WPF-Apps
But as one may understand these projects are looking all way too powerfull for my simple needs.
Here a screenshot to provide a better understanding of my needs:
It is really as simple as that. 4 rows of buttons that will never change, no other controls / functionality than sending the CommandParameter of the pressed button to the Textbox / Active Form.
Researching on that specific problem hasn't shown any problems like that. In most other SO Questions the problem is to send Data to another Window, not to send Data WITHIN the current Window.
So I don't consider this question as duplicate.
seems like a trivial task: i am building a wpf application, using MVVM pattern. what i want is dynamically change part of a view, using different UserControls, dependent on user input.
let's say, i have got 2 UserControls, one with a button, and another with a label.
in main view i have a container for that. following XAML "works":
<GroupBox Header="container" >
<local:UserControlButton />
</GroupBox>
and a UserControl element with buttons pops up. if i change it to another one, it works too.
question is how to feed that groupbox dynamically. if i put something like that in my model view:
private UserControl _myControl;
public UserControl MyControl
{
get
{
return _myControl;
}
set
{
_myControl= value;
InvokePropertyChanged("MyControl");
}
}
and change my view XAML to something like:
<GroupBox Header="container" >
<ItemsControl ItemsSource="{Binding MyControl}" />
</GroupBox>
and feed it from command with usercontrol for button or for label: nothing happens, although "MyControl" variable is set and is "invoke property changed"..
Obviously there are many ways to skin this particular cat - but to answer the question of why it doesn't work you need to look into the ItemsSource property of ItemsControl on MSDN.
The items control is designed to show multiple items, provided through an IEnumerable passed to the ItemsSource property. You are passing a UserControl, so the binding will fail.
For your example, I would change the ItemsControl to a ContentControl and bind the content to your MyControl property. This should then work.
<GroupBox Header="container" >
<ContentControl Content="{Binding MyControl}" />
</GroupBox>
However, I would strongly recommend looking into other ways of doing this - having a control in your VM breaks MVVM to my mind. Depending on what you are doing look at data templates - #Sheridan's link in the comments provides an great description of a way to do it.
Couldn't post this as a comment so adding as answer..
Have a look at this:
Implementing an own "Factory" for reusing Views in WPF
It uses DataTemplates but doesn't require the DataTemplate section for each view. If you potentially have a lot of user controls/views you wish to display or you are reusing through multiple views or you are intending to actually dynamically generate a view (versus just loading an existing user control) then this might suite your needs.
I have a WPF window displaying different self-defined Views. So far I was able to use everything I learned about MVVM :)
Now I got to a new "problem": I have 10 entities of the same view in a bigger view. These ten view-entities contain a set of controls (textbox, combobox etc.) but are all consistent.
So how do I bind these Views to a ViewModel?
I thought about having 10 instances of the ViewModel in the "higher-level" ViewModel and give the views fix-defined the instances of the VM as datacontext.
My question is now --> Is there a easier (or more convienient) way to bind many (identical) views to their viewmodels?
Code-Example:
View Model:
private PanelViewModel _panelViewModel1 = new PanelViewModel();
public PanelViewModel PanelVM1
{
get { return _panelViewModel1; }
}
View-Example:
<myControls:vwPanel HorizontalAlignment="Left" x:Name="vwPanel1"
VerticalAlignment="Top" DataContext="{Binding Path=PanelVM1}"/>
What bothers me is that I would need this logic ten times for ten views?
UPDATE:
To answer some questions: I want to show one view 10 times (in my example) I defined my own view by inheriting from UserControl. So my vwPanel inherits from UserControl. The 10 vwPanels are just placed inside a StackPanel inside a Grid.
It's not about displaying data, as you pointed out, there would be a listview or a datagrid a better place to start. It's a special case where I need this much input-controls :/
UPDATE2: What I hoped for was more like defining a List of ViewModels and Bind my 10 Views to one of this List. But this will not work will it? At least I wouldn't know how to refernce one "special" entitiy in the list out of XAML...
Typically I use implicit DataTemplates for mapping Views to ViewModels. They can go in <Application.Resources>, <Window.Resources> or even in under specific elements only such as <TabControl.Resources>
<DataTemplate DataType="{x:Type local:PanelViewModel}">
<myControls:vwPanel />
</DataTemplate>
This means that anytime WPF encounters an object in the VisualTree of type PanelViewModel, it will draw it using vwPanel
Objects typically get placed in the VisualTree through an ItemsSource property
<ItemsControl ItemsSource="{Binding CollectionOfAllPanels}" />
or by using a ContentControl
<ContentControl Content="{Binding PanelVM1}" />
If I understand your question correctly, you have a collection of something that you what to represent visually. That is, you have several viewmodels that you want to define a single view for, but show X number of times. Your example shows you using a panel as your view for the "PanelViewModel"...what is the parent item's control for the vwPanel? Assuming you're using something like a ListBox, you can define a custom DataTemplate that contains your vwPanel and assign that DataTemplate to your ListBox.ItemTemplate.
For example:
<Window.Resources>
<DataTemplate x:Key="myVMTemplate" TargetType="{x:Type myViewModels:PanelViewModel}">
<myControls:vwPanel HorizontalAlignment="Left" VerticalAlignment="Top"/>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Path=MyCollectionOfPanelVMs}"
ItemTemplate="{StaticResource myVMTemplate}" />
I haven't verified that this works.
i've been banging my head on this for the last hours...
I have a User Control called "DayItem", and i want to show it 48 times in another UserControl called "DayPanel".
Let me mention this is done in MVVM style, but i'm only experiencing, and a straight way would by fine for an answer.
I have an ObservableCollection<DayItem> in the DayPanel model, and in the Xaml there's an <ItemsPresenter />.
if i do
this.ItemsSource = DayItems;
everything show up fine.
but, i wanna be able to use those DayItems in the UI like a list... to support multi-select etc.
so i tried using a ContentControl, and set it's content to the ObservableCollection.
but it just shows the ObservableCollection object's ToString text.
so i guess i need a DataTemplete there...
but why do i need a DataTemple to show a Control?
it's already styled in it's own Xaml, i don't wanna repeat it's styling again.
or maybe i'm totally wrong, anyway i need help :x
Edit:
I got this to work, saying what DataType wasn't necessary or even possible.
and in the code behind i told the listbox, that it's ItemSource was the ObservableCollection.
now i've ran into other problems... ListBox related...
There are Gaps between each control in the ListBox, which messes up the layout
and also i need to figure out a way to select multiple items by dragging...
thanks for the help so fat
First, you need a view model for you DayItem user control. Lets call it DayItemViewModel. Also I suppose you DayPanel also has a view model called something like DayPanelViewModel. Then, you DayPanelViewModel would expose a collection of DayItemViewModel instances:
public class DayPanelViewModel
{
public ObservableCollection<DayItemViewModel> DayItems { get; set; }
}
Then, in your DayPanel.xaml:
<UserControl x:Class="DayPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<DataTemplate x:Key="DayItemTemplate"
DataType="{x:Type my:DayItemViewModel}">
<my:DayItem />
</DataTemplate>
</UserControl.Resources>
<Grid>
<ListBox ItemsSource="{Binding DayItems}"
ItemTemplate="{StaticResource DayItemTemplate}" />
</Grid>
</UserControl>
Try using ListBox, since that implements multiselect...
Also it might be wise (for MVVM) if you do not contain DayItems, but DayItemModel's in your DayPanelModel, and set the ListBox's ItemTemplate to present each DayItemModel with a DayItem.