I'm working on my first project in WPF/XAML, and there's a lot I've not figured out.
My problem is simple - I need a window that has a bunch of fields at the top, with which the user will enter his selection criteria, a retrieve button, and a data grid. When the user clicks on the button, a query is run, and the results are used to populate the grid.
Now the simple and obvious and wrong way to implement this is to have a single module containing a single window, and have everything contained within it - entry fields, data grid, the works. That kind of mangling of responsibilities makes for an unmaintainable mess.
So what I have is a window that is responsible for little more than layout, that contains two user controls - a criteria control that contains the entry fields and the retrieve button, and a data display control that contains the data grid.
The question is how to get the two talking to each other.
Years back, I would have added a function pointer to the criteria control. The window would have set it to point to a function in the display control, and when the button was clicked, it would have called into the display control, passing the selection criteria.
More recently, I would have added an event to the criteria control. I would have had the window set a handler in the display control to listen to the event, and when the button was clicked, it would have raised the event.
Both of these mechanisms would work, in WPF. But neither is very XAMLish. It looks to me like WPF has provided the ICommand interface specifically to accommodate these kinds of connection issues, but I've not yet really figured out how they are intended to work. And none of the examples I've seen seem to fit my simple scenario.
Can anyone give me some advice on how to fit ICommand to this problem? Or direct me to a decent explanation online?
Thanks!
MVVM is the prevalent pattern used with WPF and Silverlight development. You should have a read up on it.
Essentially, you would have a view model that exposes a command to perform the search. That same view model would also expose properties for each of your criteria fields. The view(s) would then bind to the various properties on the view model:
<TextBox Text="{Binding NameCriteria}"/>
...
<Button Command="{Binding SearchCommand}".../>
...
<DataGrid ItemsSource="{Binding Results}"/>
Where your view model would look something like:
public class MyViewModel : ViewModel
{
private readonly ICommand searchCommand;
private string nameCriteria;
public MyViewModel()
{
this.searchCommand = new DelegateCommand(this.OnSearch, this.CanSearch);
}
public ICommand SearchCommand
{
get { return this.searchCommand; }
}
public string NameCriteria
{
get { return this.nameCriteria; }
set
{
if (this.nameCriteria != value)
{
this.nameCriteria = value;
this.OnPropertyChanged(() => this.NameCriteria);
}
}
}
private void OnSearch()
{
// search logic, do in background with BackgroundWorker or TPL, then set Results property when done (omitted for brevity)
}
private bool CanSearch()
{
// whatever pre-conditions to searching you want here
return !string.IsEmpty(this.NameCriteria);
}
}
Related
what I think I face is a problem of design - one I assume has been solved many times by many people since it seems like it would be very common.
I'm using WPF with XAML and a simple MVVM approach, for the record.
My intention is to create a TreeView, using the MVVM design pattern in WPF.
I have a data model which contains two classes: Scene and Character. Each Scene contains many Characters.
I have created a CharacterViewModel, which is fairly simple (and works fine). Naturally this wraps around the existing Character class.
The Scene class is where I'm getting confused. As I understand it, the SceneViewModel should wrap around the Scene class, just as the CharacterViewModel did for the Character class. But the difference is that Scene contains a list of Characters and thus adds exta complications.
The two options seem to be as follows:
Option 1: Scene contains List of Character and so SceneViewModel also will have that as part of it.
Option 2: Scene contains List of CharacterViewModel and so SceneViewModel will also have that as part of it.
I'm not sure which one to go for to be honest. I suspect it's the second (and this tutorial seems to agree (example 6 is the heading for the section I'm referring to). The first option seems like it would make things really weird (and also why created the CharacterViewModel at all?) but the second seems strange because it seems to muddy the waters regarding what should be in the model part of the program and what should be in the view model part of the program.
I hope I've explained my problem and I also hope someone can offer some help.
Thanks.
Let me first address this statement:
...the SceneViewModel should wrap around the Scene class, just as the CharacterViewModel did for the Character class.
This isn't exactly true. View model should be created for each view. There may be a one-to-one with your model classes, but that isn't a strict part of the MVVM idea. One view may need to present data from multiple "root" model elements (model elements that don't have an explicit relationship like the parent-child relationship in your application), or you may need to have multiple views for a single model element. And to elaborate further, each view model should ideally be isolated from the view technology as much as possible (i.e. a single view model is sufficient to create a WinForms view or a WPF view or an HTML view, etc).
For example, you may have a view that displays data from your Scene class. That view may also display some data for each Character in your Scene. The user may be able to click on a Character and open a view just for that Character (e.g. a popup). In this case, there may be separate view models to represent the Character in the root view and the popup. I tend to name my view model classes according to the root of the view. For an application like yours, I would have something like SceneViewModel and SceneCharacterViewModel (or SceneViewModel_Character, or CharacterInSceneViewModel -- any of these names conveys that the class is for representing a Character in a view for a Scene). This would differentiate that view model from the popup view (which would be Character-centric and would be named something like CharacterViewModel (or even CharacterDialogViewModel or CharacterPopupViewModel or CharacterEditorViewModel).
Keeping collections in sync between the model and view model is annoying but often necessary. Not always necessary, mind you -- there will be cases in which you'll find there are no additional view-model features that you need to add to a model, so it's perfectly acceptable for the view to reference the model directly in this case.
An example of keeping a model collection and view model collection in sync: Suppose your root SceneView has a button for each Character. That button will display a popup for the Character. Suppose further that the Character popup doesn't have a similar button because then it would allow the popup to open another popup (etc). You may want to use an ICommand implementation so that you can just bind the button to the command. It's definitely not appropriate for the ICommand instance to be in the model (even though the command may call a public method on the model). The appropriate place for this would be in the view model for the Character in the Scene view (not the view model for the Character in the popup). For every Character in the model, you would need to create a view model that references the Character and stores additional view-model stuff (the ICommand object).
This means that, as Characters are added/removed from the Scene, you need to create view models specifically for those Characters within the Scene view model. I would typically do this:
At construction time (or whatever time the view model initially receives the model), create a view model for each child object. Put those view models into a public property with a type of something like ReadOnlyCollection<SceneCharacterViewModel>. Your view will bind to that collection.
As child objects are added to the model (either internally or through a public method on the model), the model should notify the view model in an appropriate way. Since the model shouldn't have a direct reference to the view model (not even through an interface -- a model should be completely functional even in a non-UI context, in which there is no view model), the most appropriate way is to use events. You can do this a couple of ways:
Expose events from your model like CharacterAdded, CharacterRemoved or even CharactersUpdated (the last of these would be able to communicate either an add or a remove using a single event)
ObservableCollections (or ReadOnlyObservableCollections), which are most commonly used in view models, can also be used in models, in which case all the events are already available to you. The downside to this is that processing the events off of these collection types isn't the easiest thing.
A third option that is totally different: If your view model or command instance is directly invoking a method like sceneModel.AddCharacter(newCharacterModel), then you can just update your view model immediately after this line without needing any events. I often find myself starting this way because it's simple, but I almost always end up using one of the previous two techniques instead, as those techniques allow the model to notify the view model even in cases where the update is happening internally (e.g., in response to a timed event or asynchronous operation that is controlled by the model).
All of that being said, here's what a "pure" MVVM architecture would look like for your application. Purity can come at the expense of simplicity, so sometimes it's better to take some shortcuts here and there. One common shortcut: In WPF, it's often easier just to put all of your child widget content in the ItemTemplate of the ItemsControl that is being used to diplay your children, rather than creating a separate UserControl for the children.
I guess from your explanation Scene is the Model and SceneViewModel would wrap additional view related functionality to your model in the view model. Same applies for CharacterViewModel.
Whatever your view needs to display you would have a SceneViewModel with a list of CharacterViewodel or vice versa. Or like mentioned your could build up a tree structure with your ViewModels.
My personal view of things is, it is important to stay in the ViewModel universe. So when your construct a view model you would inject your model via a service and build up your ViewModel and only have lists with view models. You would need to do some mapping but there are useful frameworks like automapper, etc already available. But remember there are no hard rules with MVVM.
I didn't understand your choices, However, I think you just need one View Model and it should contain an ObservableCollection of the Scenes. I name it SceneViewModel:
public class SceneViewModel
{
public SceneViewModel()
{
Scene m1 = new Scene() { Name = "Scene1", Characters = new ObservableCollection<Character>() { new Character() { Name="C1" }, new Character() { Name = "C2" } } };
Scene m2 = new Scene() { Name = "Scene2", Characters = new ObservableCollection<Character>() { new Character() { Name = "D1" }, new Character() { Name = "D2" } } };
Scene m3 = new Scene() { Name = "Scene3", Characters = new ObservableCollection<Character>() { new Character() { Name = "R1" }, new Character() { Name = "R2" } } };
_scenes = new ObservableCollection<Scene>() { m1, m2, m3 };
}
ObservableCollection<Scene> _scenes;
public ObservableCollection<Scene> Scenes { get { return _scenes; } set { _scenes = value; } }
}
Scene will have an ObservableCollection of Characters
public class Scene : INotifyPropertyChanged
{
ObservableCollection<Character> _characters;
public ObservableCollection<Character> Characters { get { return _characters; } set { _characters = value; RaisePropertyChanged("Characters"); } }
string _name;
public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
and at last, Character:
public class Character : INotifyPropertyChanged
{
string _name;
public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
View
<TreeView DataContext="{Binding}" ItemsSource="{Binding Scenes}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Characters}">
<TextBlock Text="{Binding Path=Name}"></TextBlock>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
public MainWindow()
{
InitializeComponent();
DataContext = new SceneViewModel();
}
I have this code in VB.Net, in windows forms. I need to wait for the user's choice, but keeping the UI responsive, so the user can select a option from a ListBox. The listbox_SelectionChanged event will set a Boolean named selectedElement to true, so the execution continues. In WPF I have found that it is possible to do it with threading but I am not sure how to do it. Any advice? Thanks :)
Do
System.Windows.Forms.Application.DoEvents()
Loop Until selectedElement
That is a terribly bad way to code. Even in winforms.
First of all, if you're working in WPF you really need to understand The WPF Mentality.
in MVVM WPF, your ViewModel should control the actions executed by the application upon reacting to user input.
Generally speaking, this is how you deal with "wait for the user to select an item in a ListBox in WPF:
XAML:
<ListBox ItemsSource="{Binding SomeCollection}"
SelectedItem="{Binding SelectedItem}"/>
ViewModel:
public class SomeViewModel
{
public ObservableCollection<SomeData> SomeCollection {get;set;}
//Methods to instantiate and populate the Collection.
private SomeData _selectedItem;
public SomeData SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
//PropertyChanged() is probably desired here.
UserHasSelectedAnItem(); //Method invocation
}
}
private void UserHasSelectedAnItem()
{
//Actions after the user has selected an item
}
}
Notice how this is fundamentally different from the RefreshTheUIHack(); loop approach.
There's no need to "loop" anything because WPF (and also winforms) already have an internal
"message loop" which listens for user input and raises events when needed.
I suggest you read the above linked answer and related blog posts to understand what you need in order to move from the traditional, procedural winforms approach into the DataBinding-based WPF mentality.
I was going through the Caliburn Micro documenation here. Simultaneously, I was trying to put up some rough code for experiment. I am a little confused about how to activate item using a container and how to pass an object to the ViewModel that we are activating.
Lets consider a master/detail scenario. The master contains a list (say datagrid) and the details contain specific row from the master for update(say tab item inside tab control). In the documentation (for ease of understanding), I believe the detail ViewModel was directly instantiated using code like this
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive {
int count = 1;
public void OpenTab() {
ActivateItem(new TabViewModel {
DisplayName = "Tab " + count++
});
}
}
So, to apply the above fundamental concept in real world app, we need to instantiate the DetailViewModel (TabViewModel above) using container(say MEF). The challenge then is to know whether the particular DetailViewModel is already opened in the TAB Control. The immediate crude thing that came to my mind was maintaining a List of the Opened Tabs (DetailViewModels). But then we are again referencing DetailViewModel in the MasterViewModel defeating the purpose. Is there any options available to solve this issue.
The second thing that is troubling me is how to pass the Objects from MasterViewModel (Selected Detail Item) to the DetailViewModel. If we use the EventAggregator here then each of the opened DetailViewModels will receive the event which I am not sure how to handle.
If anyone can throw some light on the above two issues, I would be grateful
Update:
The Master is Conductor like this
public class MainViewModel : Conductor<IScreen>.Collection.OneActive, IShell {
....
}
And the detail is defined like this
public class TabViewModel : Screen {
....
}
Both are in the same Window.
I'm not sure exactly what the issue is. In your conductor of many, you have an Items collection provided by Caliburn.Micro. When you come to display a detail view, you can check this collection for the existence of that detail view (using the primary key which you have from the master view).
If the item is already in the Items collection then just activate it (using the ActivateItem method). If the item isn't in the collection, then instantiate it (presumably using a factory if you're using MEF), and add it to the Items collection, and then activate it.
I'm about to redo an old WinForms application as a WPF application. The core of this applicaton is a custom "grid" component. I'd like some ideas on the best way to do this as a WPF component.
The application displays a grid of data for different countries/sectors. Each cell of the grid displays different information (e.g. graph, image) depending on the available data for that country/sector.
I have a domain model assembly that I want to keep clean - to maximize reuse. The structure is as follows:
Table
Continents
Countries
Sectors
Data[country, sector]
The grid displays countries down the left and sectors across the top.
In the current application, the grid component has a (POCO) Table property and an Refresh() methods to manually redraw it. So, if the Table is updated, the parent of the grid component refreshes it. The grid component also has a number of events that are fired if a continent, country or cell is clicked - so that the parent can response with pop-up menus, etc.
This all works fine.
However, I'm wondering whether this is the correct model to use for a WPF application. Looking at many of the WPF example, they support data-binding, etc. But, it's not clear, from the simple examples, how I might bind a complex object to my components - or whether it would even be worthwhile.
Also, the WinForms component is completely custom drawn - there are no sub-controls (e.g. Labels) in use. Would it be better to use a WPF user control and build the table from a GridLayout and lots of Label, Shape, etc controls? In practice, they are maybe 20 rows and 20 columns in the grid, and the user regular removes and adds countries/sectors (rows/columnms) while using the application.
My immediate goal is to make sure my design plays well in the WPF eco-system, but I have a secondary goal of learning how to do things in a WPFy way - given this is my first WPF app. I'm pretty on top of the use of building a general WPF app - it's just the custom control stuff that remains a little fuzzy (even after reading around it a little).
Any insights/guidance would be appreciated.
You definitely want to adapt the MVVM approach, as outlined by Josh Smith. Practically, this means that your custom grid component will be contained in it's own View. Backing the view will be your ViewModel, where you will define an ObservableCollection of objects containing your data. These objects will probably come from your Model. This interaction is shown below:
Models:
public class TableData
{
public string Country { get; set; }
public string Continent { get; set; }
public object Sector { get; set; }
}
public class TableManager : ITableManager
{
public Collection<TableData> Rows;
public void GetData()
{
this.Rows = new Collection<TableData>();
this.Rows.Add(...
}
}
ViewModel:
public class TableViewModel
{
private ITableManager _tableManager;
public TableViewModel() : base(new TableManager())
{
}
// for dependency injection (recommended)
public TableViewModel(ITableManager tableManager)
{
_tableManager = tableManager;
_tableManager.GetData();
}
public ObservableCollection<TableData> Rows
{
get { return _tableManager.Rows; }
}
}
View:
<ctrls:CustomDataGrid
ItemsSource={Binding Rows}
AutoGenerateColumns=True
>
<!-- Use AutoGenerateColumns if the # of sectors is dynamic -->
<!-- Otherwise, define columns manually, like so: -->
<DataGridTextColumn
Width="*"
Header="SectorA"
Binding="{Binding Country}
/>
</ctrls:CustomDataGrid>
I used CustomDataGrid in the view because I assume you're going to subclass your own DataGrid. This will allow you to override events to customize the DataGrid to your liking:
public class CustomDataGrid : DataGrid
{
public override Event...
}
This is my first attempt at MVVM. My application's core is loosely based Josh Smith's msdn article. And I am also using the mvvm light framework.
I have a main window containing a command list area and a workspace area which shows usercontrols/views as tabitems, each usercontrol has a corresponding viewmodel. The mainWindow also has a viewmodel containing my command list, and the workspace viewmodels have a base workspace viewmodel.
My default view has a master datagrid, of MappingSets, that can have one selected item. The commands launch new tabitems with views that handle MappingSet detail based on that selected item. I have a View/ViewModel that, depending on the command used should return either a tabitem for creating a new MappingSet with no existing data, or a tabitem containing the detail of the selected item for editing, or a tabitem containing detail the selected item as the base for a new MappingSet.
Having Set the scene, what I have not managed to work out is command dependent way to pass parameters, such as the identifier of the selected MappingSet object, to instantiate my viewmodel in one of the three states mentioned above? For instance would the mvvmlight messenger be appropriate for this task?
This is a perfect scenario for the messenger/eventaggregator. However, your message chain might be a bit convoluted. From what I'm understanding, your Main window holds a list of commands (like a menu or a ribbon). Here is how I see the chain of events.
You select a MappingSet from the datagrid, this causes a MappingSetSelected message to be fired (with a payload of the selected MappingSet)
The main window listens for that message and stores the currently selected MappingSet
When the user clicks the button a "EditMappingSet" or "CreateNewMappingSet" message is fired (or if the Window is responsible for creating the new views, it creates them itself).
If there are only three options, you could have them binding to three different commands and within the commands do the passing of your self-defined variable.
private RelayCommand _openMappingSetCommand;
//Command that one of your options is bound to
public ICommand ViewMappingSetOption1
{
get
{
if (_openMappingSetCommand == null)
{
_openMappingSetCommand = new RelayCommand(param => this.DoTabRequest("your parameter");
}
return _openMappingSetCommand ;
}
}
// Method that creates your viewmodel
private void DoTabRequest(parameterType parameter)
{
WorkspaceViewModel viewModel = null;
if (viewModel == null)
{
viewModel = (WorkspaceViewModel)Activator.CreateInstance(typeof (viewModelType), parameter);
this.Workspaces.Add(viewModel);
}
this.ActiveWorkspace = viewModel;
}
Then allow for that parameter on the constructor of your viewmodel and do whatever else you need based on that.