WPF: Buidling my own paging itemlist....;)? - wpf

for reasons based on storabiligty I have the following objects in a XAML graph:
A WorkArea,
Containing WorkSheets,
Containing WorkItems
WorkArea, workSheets are ItemsControl instances.
To start: The reason that I am not using standard elements here is that mine are going to get loaded / saved - they represent a business contect (actually the work area of a trading application), and I want those to have as few "surplus" elements as I can. I especially do not want to be tied into user level controls that are from a third party and regularly changing dll names (during upgrades - the major version is encoded there) and I am not sure I will not replace them at all, so I rather go with my own "slim" objects.
The WorkArea corresponds to a window (actually there is a WorkAreaWindow that will take the WorkArea as ContentItem.
The WorkSheets are supposed to work like a TabControl - you can switch between them.
How do I do that? ;)
I get the impression that with the templating mechanisms I could possibly "visuall wrap" the WorkSheets as pages in a TabControl, but I am pretty much totally lost on the how. Anyone can enlighten me?
Here is how far I got:
My Herarchy is WorkArea -> WorkSheet(s) -> WorkItem(s)
WorkArea should be presented as a TabControl, with one tab per WorkSheet.
WorkArea:
<local:WorkArea x:Name="WorkArea">
<local:WorkArea.Template>
<ControlTemplate>
<TabControl>
<ItemsPresenter />
</TabControl>
</ControlTemplate>
</local:WorkArea.Template>
<local:WorkArea.ItemTemplate>
<DataTemplate>
<TabItem Header="{Binding Path=Title}">
<ContentPresenter />
</TabItem>
</DataTemplate>
</local:WorkArea.ItemTemplate>
<local:WorkSheet Title="Markets">
<local:WorkTile local:WorkSheet.Row="2" local:WorkSheet.Column="3">
test-11
What I can see now is a TabControl, with one Tab. No text, all content in the one tab. Anyone an idea how to split this further?

You should carefully read Josh Smith's introduction to MVVM here and look at the demo application source code. The demo application is almost exactly what you are asking for. It generates a tabbed interface dynamically based on custom classes for "contacts" data using data templates, observable collection binding and tabcontrol/tabitem. Some of the MVVM and commanding stuff might not be your thing, but a portion of the code does what you are looking for. The XAML has no code behind at all. You would simply set that data context of your window to your work area class instance that would have an observable collection of worksheets which in turn have an observable collection of workItems and item/data templates will do everything.

Related

Add control to logical-tree parent

My Main Window is a relatively simple DockPanel:
<DockPanel>
<!--Bottom row-->
<Border DockPanel.Dock="Bottom">
<DockPanel DockPanel.Dock="Bottom">
<!--Detector Indicator-->
<views:DetectorIndicatorView DataContext="{Binding DetectorViewModel}" DockPanel.Dock="Left"/>
<!--Logo-->
<Image DockPanel.Dock="Right" HorizontalAlignment="Right" Source="/Resources/Images/Logo.png"/>
</DockPanel>
</Border>
<!--Main display-->
<views:TabControlView DataContext="{Binding TabsViewModel}"/>
</DockPanel>
Inside the TabControl's SelectedContent there's a viewmodel for each tab, and each one of those can host a variety of viewmodels.
At one point, the tree basically looks like:
<MainWindow>
<TabControl>
<ExamTab>
<EditExam/>
</ExamTab>
</TabControl>
</MainWindow>
And on the EditExam page, I want a toolbar to appear in the Bottom row of the MainWindow. The toolbar buttons will be bound to commands ont he EditExamViewModel.
Is there a way I can "inject" an inner control like this into the "outer template" (i.e., the main window)?
The only way I can think of is to take the bottom row out of the MainWindow and paste it into each individual view, identical except for the one instance in EditExamView. Is that the only way?
Is there a way I can "inject" an inner control like this into the "outer template" (i.e., the main window)?
No, there is not, at least not in XAML.
You could it programmatically by for example getting a reference to the parent window in the view using the Window.GetWindow method, or by raising an event or send a message from the "tab" view model to the window view model or window using an event aggregator or a messenger.
What you describe is common in modern applications. Even menus are often swapped depending on the active view. However, there is no built-in way of doing this in XAML or WPF. The easiest way to solve this is
[...] to take the bottom row out of the MainWindow and paste it into each individual view, identical except for the one instance in EditExamView. [...]
The hardest way is to develop all that is needed for this scenario yourself including
Creating views and view models as data context dynamically
A concept to define areas where your views should be injected
Swapping in and out dependent views including their view models
Communication between views for synchronization or updating
Managing activation and deactivation of your exam tabs
Custom commands that you can wire accross views to your tab view
...
This is a lot of code that you might not be able to write on your own. But you are not alone, because there are application frameworks intended to bridge the gap between UI frameworks and your code like Caliburn Micro or Prism. These frameworks enable you to skip a lot of boilerplate code and provide services and mechanisms for various issues, but at the cost of learning them and some customization to fit your needs.
Prism as an example provides the following solutions to the issues above
A dependency injection container
Regions that define areas within your application
A rich region navigation service
An event aggregator for communication
Region adapters with active awareness
Composite Commands that can be used across views
...
This looks overwhelming at first, but the more you become familiar with a framework of your choice, the easier application development gets, as you will reuse concepts and components. At this point I recommend you not to re-invent the wheel. At first, you could use the easy solution and start getting familiar with any framework and gradually migrate your application.
So actually I’ve come up with a pretty good and simple solution, which is simply:
<ContentControl Content=“{Binding TabControlViewModel.Tabs[SelectedIndex].CurrentViewModel.ToolbarViewModel}”/>
If the current view model doesn’t have a toolbar view model then it’ll just evaluate to null and show nothing. Then I can define a data template for ToolbarViewModel and we’re all set.
If I wanted to enable various pages to have different things in the bottom bar, I could have ToolbarViewModel be some more general type (and probably a different name), possibly whatever my view model base is, and then any page’s VM could be any type of view model I want, and I could set the DataTemplate. The only caveat would be I don’t think I could have more than one template for any given VM, but I could get around that by creating simple subclasses for each way I’d want it to render so I could have different templates.

Several WPF- / XAML-Files performing better than a big one?

I was talking recently to another GUI developer that was looking at one of my WPF applications. He suggested to break down my very large WPF views (files) into several smaller views. According to him this should promote loading performance as elements that are loaded from UserControls in separate files are rendered only when they actually come into view.
In my main view I currently have over 10k lines of XAML. As the main part of this view is a big TabControl with several large TabItems it would be easy to transfer the content of each TabItem into a separate file. It is true that most of the Tabs will not be used by every user or at least not directly after startup.
However, I was not able to verfy his claim by searching the web. Whenever I search the internet for WPF performance optimization I find all kinds of suggestions, but splitting up large XAML files into smaller ones was never mentioned in any way there.
For me, personally, having one big XAML per view is more comfortable and reusability is really not a problem in my case. So now I am wondering:
Would it really make sense to break my large views up into serveral small XAML files for each part that might not be needed at first?
Would it really make sense to break my large views up into serveral small XAML files for each part that might not be needed at first?
From a maintainability and reusability point of view, yes. But whether you define content of each TabItem inline or in a separate UserControl won't matter from a performance point of view.
So you won't gain any performance benefits of doing this:
<TabItem>
<local:UserControl1 />
</TabItem>
...versus doing this:
<TabItem>
<Grid>
...
</Grid>
</TabItem>
You should consider adopting the MVVM design pattern and bind the ItemsSource property of the TabControl to an IEnumerable property of a view model and use DataTemplates to define the appearance of each item rather than defining everything inline though. Doing this, only the contents of the selected TabItem will be loaded into the visual tree.

What is the right way to approach the TabControl with complex views?

My application is for medical clinics and I select a patient and then there are several Tabs to choose from, Patient Info, Family History and so on. I started out with all tabs in the MainView but have come to the conclusion that each tab needs to be on separate Views because of the complexity of the screens.
How then do I share the Patient info across multiple views? All screens relate to the Patient in some way and I have gone down the road of MainViewModel hosting all the data but that seems like a bad idea because I manipulate data on separate views and need to get the updates back to the MainViewModel. What is the right approach, I have searched the internet for days looking at different architecture and had little to no luck finding any type of similar architecture.
What I did so each ViewModel could access the Patient data is use the code below.
<TabControl Grid.Row="2" x:Name="TC">
<TabItem Header="Patient Info" IsEnabled="{Binding IsPatientSet}" DataContext="{Binding}">
<view:TabPatientView DataContext="{Binding ElementName=TC, Path=DataContext}" />
</TabItem>
<TabItem Header="Clinical Worksheet" IsEnabled="{Binding IsPatientSet}">
I bound each ViewModel to the MainViewModel since each ViewModel was just one aspect of Patient Info. Works great!
The architecture of a program depends on different varibles and there is no one correct answer which I can give you.
However, I can propose some ways to handle your issue:
Keep a Master ViewModel (which is what you are doing now). Create a ViewModel for each View and use a common class (e.g Service) to share patient info between the View models. In this case, service may also expose an event which can be raised when patient info is changed.
Use a light weight WPF infrastructure like MVVM Light Toolkit to share changes in patient info by using the Messenger instance. (Like option 2, but using free 3rd party infrastructure)
Use PRISM with DI.
If all of yours View-ViewModel code resides in the same DLL, you might consider using option 2, or 3.
If some of the View-ViewModel resides in different assemblies you might consider using option 4 which presents the EventAggregator design pattern.
Hope this help to get you on the right path.

Split view into two separate views

I'm building a WPF application which very simplified looks something like this:
I have an ApplicationView which holds the menu and a ContentControl.
The ContentControl binds to the property CurrentViewModel which is set by the menu and rendered by its related View (Views and ViewModels are coupled by DataTemplates defined in the App.xaml).
I found this approach on Rachel Lim's blog
So in this example my View contains a list of duties as well as a "Details" window of the currently selected duty.
This setup works fine, but I think my ViewModels are getting too fat!
The non-simplified version of this ViewModel is up at around 500 lines of code, for handling:
Initializing filters
Logic for filtering list
Displaying duty details
Add/Update/Cancel/Delete logic
Now I'm very new to WPF but that seems like too much code, yea?
And it will be even bigger before I'm finished with it.
Anyways, I was thinking that I could split the ViewModel into two separate ViewModels; one for holding list and filters and one for showing the details. But how is this best accomplished?
I have thought of two approaches, but don't know which is preferable:
Create a DutyMasterView whose sole purpose is to hold two ContentControls for the actual Views (ie DutyListView and DutyDetailView each with their own ViewModel)?
I'm using MVVM Light as my framework so I suppose I could use the messaging service to tell the DutyDetailViewModel which Duty to display, right?
Alternately create a DutyMasterViewModel which exposes the selected duty.
Ditch the DutyMasterView and nest the DutyDetailView in the DutyListView.
Does it make sense to split my ViewModel into two or should I just stick with my fat ViewModel?
If splitting the ViewModel is recommended which of my suggestions makes most sense?
Are there other approaches that I should consider?
If you're still looking for opinion, I'd do it almost like you mentioned in point 1 but you don't need any messaging.
You create two VMs. Let's say DutiesVM and DutyDetailVM. DutyDetailsVM contains just some string properties for ID and Name.
In DutiesVM you create two properties:
ObservableCollection<DutyDetailVM> DutiesList
DutyDetailVM SelectedDuty
Your DutiesView can look like this:
<DockPanel>
<v:DutyDetailV DockPanel.Dock="Right" DataContext="{Binding SelectedDuty}">
<ListBox ItemsSource="{Binding DutiesList}" SelectedItem="{Binding SelectedDuty}"/>
</DockPanel>
Now you can create ListView ItemTemplate that binds to DutyDetailVM Properties.
is usercontrol that defines the DutyDetail view. Selecting the item in the list updates the details control automatically.
That's just the sketch but I think you can get the point from it.

How to create databinding over two xaml files?

I am trying to come to a working understanding of how databinding works, but even after several tutorials I only have a basic understanding of how databinding works. Thus this question might seem fundamental to those more familiar with silverlight. Even if it is trivial, please point me to some tutorial that deals with this problem. All that I could find simply solved this via adding the data binding on a parent page.xaml (that i must not use in my case).
For the sake of this example let us assume, that we have 5 files:
starter.cs
button1.xaml + codeBehind
button2.xaml + codeBehind
The two buttons are generated in code in the starter(.cs) file, and then added to some MapLayer
button1 my_button1 = new button1();
button2 my_button1 = new button2();
someLayer.Children.Add(my_button1);
someLayer.Children.Add(my_button2);
My aim is to connect the two buttons, so that they always display the same "text" (i.e. my_button1.content==my_button2.content = true;). Thus when something changes my_button1.content this change should be propagated to the other button (two way binding).
At the moment my button1.xaml looks like this:
<Grid x:Name="LayoutRoot">
<Button x:Name="x_button1" Margin="0,0,0,0" Content="{Binding ElementName=x_button2, Path=Content}" ClickMode="Press" Click="button1_Click"/>
</Grid>
But everthing that i get out of that is a button with no content at all, it is just blank as the binding silently fails.
How could I create the databinding in the context I described? Preferably in code and not XAML ;)
Thanks in advance
The chunk of documentation you need to read is this: XAML Namescopes
Your button1 xaml has a binding looking for an element with the name "x_button2". However in a real application there can be many controls which in turn have nested controls. All of these controls have all manner of UI elements some of which may have names.
It would be impossible to get anything done if all names throughout the entire application had be unique. Yet that would need to be true if it were for your button1 to be able to hunt down the existence of another control somewhere in the visual tree outside of that which it actually knows (its own xaml).
Hence each loaded Xaml document exists in its own "namescope" and the search for other elements with other names is limited to that "namescope".
The are various solutions to this problem depending on what you real requirements are as opposed to the simplified problem in your question.
Typically you give each of your controls a DependencyProperty to which the inner button Content property binds. In "MapLayer" as call it, could then bind the propert on one of your button controls to the other.

Resources