ViewModel communication question - wpf

Imagine I have a UserControl that shows a parking lot (my favorite analogy) with cars of different colors. You can select a car, and in a separate UserControl (in a separate project) statistics of the selected car are displayed.
Now a user wants a button on the car statistics UC, 'Next car of same color'. When selected it should show the statistics of the next car (top to bottom, left to right) on the parking lot with that same color.
So if that makes sense, on to the question.
I am currently using MVVM Lite to send a message containing the selected car from the parking lot UC to the car statistic UC. All is good. Now though, with this new feature request, what should I do? The statistic UC needs to request the next car from the parking lot UC.
Would this be a good place to use dependency injection? Or is there another better approach?

If I am getting you right, what you want is a Command with proper CommandParameters.
public class Car
{
public Car(ParkingLot lot)
{
_parkingLot = lot;
}
public string Color { get; set; }
public ParkingLot ParkingLot
{
get
{
return _parkingLot;
}
}
private ParkingLot _parkingLot;
}
public class ParkingLot : ObservableCollection<Car>
{
public Car SelectedCar { get; set; }
public ICommand ShowNextCarCommand {
get
{
if (_showNextCar == null)
{
_showNextCar = new DelegateCommand(OnShowNextCar);
}
return _showNextCar;
}
}
private void OnShowNextCar()
{
string currentColor = SelectedCar.Color;
//Write proper logic to get the next Car. Here you got the currently selected car with you and the color
SelectedCar = this.NEXT(s => s.Color == currentColor); //Write the NEXT() logic
}
ICommand _showNextCar;
}
Now it is a matter of setting Button.Command="{Binding ParkingLot.ShowNextCarCommand}" now you got your control over to the ParkingLot viewmodel class and find the Next same colored car and set it again to SelectedCar property. I assume you will have RaisepropertyChanged in all these properties. I use simple DelegateCommand of Prism

I would use a Controller as mediator between the two ViewModels (ParkingLotViewModel and StatisticsViewModel). In your case the Controller is responsible to synchronize the selected car and it is responsible to pass the ‘Select next car of same color’ command to the ParkingLotViewModel.
The sample applications of the WPF Application Framework (WAF) show how this can work.
.
jbe

Related

Fill a combobox in WPF with a list of web services

I have a control like this:
<ComboBox x:Name="ComboTipo"
Height="23"
SelectionChanged="ComboTipo_SelectionChanged"
Width="450"
Canvas.Left="609"
Canvas.Top="26" />
And my code is:
ComboTipo.DisplayMemberPath = "Descripcion";
ComboTipo.SelectedValuePath = "IdTipoPersona";
ComboTipo.ItemsSource = myWebServices.dameTipos();
My web services returns a list for this object, this class is created in automatic when i add the reference to the web services:
public partial class TipoPersona {
private short idTipoPersonaField;
private string descripcionField;
/// <comentarios/>
public short IdTipoPersona {
get {
return this.idTipoPersonaField;
}
set {
this.idTipoPersonaField = value;
}
}
/// <comentarios/>
public string Descripcion {
get {
return this.descripcionField;
}
set {
this.descripcionField = value;
}
}
}
But the problem is:
The combobox displays the data types for each element of the list, and i want display the Descripcion.
Can you help me plis! Thanks
What does IdTipoPersona look like? Is it a class you created? If so, you may need to reference the property that you want displayed. It would look something like this:
ComboTipoPersona.SelectedValuePath = "IdTipoPersona.Text";
Where Text would be replaced by the property. It is really hard to judge otherwise what is going on with knowing a little more about the object structure that myWebServices.dameTipos() returns.
EDIT
Ok I was able to simulate your problem and simulate a solution as well.
Your issue is in the Tipos class. There are a couple of things necessary when binding to a combobox with a custom class.
First off, you will want to add accessors and mutators (getters and setters) to IdTippoPersona and Descripcion.
You should add a constructor that assigns to those properties with parameters.
It is usually a good idea to add a default constructor.
The finished code will look like this:
public class Tipos
{
public int IdTipoPersona { get; set; }
public string Descripcion { get; set; }
public Tipos(int id, string descripcion)
{
IdTipoPersona = id;
Descripcion = descripcion;
}
}
I found the asnwer if someone needs it.
We need create a class intermediate class but we were working with entity framework, for this way, we need add the intermediate class like complex type in my model (entity framework).
And also we need override this class.
And it works so well.
Thanks for all #Goody

Update a wpf label periodically

I am new to WPF and C# im trying to understand how can I update a UI element from a BL class (to keep a seperation between the logic and the UI) the bl gets periodic updates from a c++ network component and should update the form once a new argument comes in (I read on the msdn website but I want to see some concrete examples to make sure I got it right)
Because of your gets periodic updates from a c++ network component comment, I am assuming that you already have a system to update your property. I would expose that property from your business class in a view model class, a class with public properties and ICommand functions designed specifically to supply all of the required data to a view, or UserControl.
To be honest, I wouldn't have that (or any) functionality in a business class (depending what you mean by business class)... I'd personally put it straight into the view model, or have a manager/service class that exposed it.
If you insist on keeping it where it is, you'll have to implement either an event or a delegate in your business class so that users of that class can be alerted as to when the value changes. Then you could simply attach a handler to your event/delegate from the view model class and easily update the exposed property whenever the actual property changes.
So it would go a little something like this... in your business class (I am assuming that your value is an int, but you can change this if it is incorrect... the principal is the same):
public delegate void FieldUpdate(int value);
public FieldUpdate OnFieldUpdate { get; set; }
...
private int field;
public int Field
{
get { return field; }
set
{
if (value != field)
{
field = value;
if (OnFieldUpdate != null) OnFieldUpdate(field);
}
}
}
Then in your view model:
private YourBusinessClass instance = new YourBusinessClass();
public YourBusinessClass Instance
{
get { return instance; }
set { instance = value; NotifyPropertyChanged("Instance"); }
}
Attach a handler:
instance.OnFieldUpdate += OnBusinessClassFieldUpdate;
...
public void OnBusinessClassFieldUpdate(int value)
{
Instance = value;
}
Now whenever the field is updated in the business class, the view model (and data bound UI controls) will automatically update via the delegate.

How to avoid code duplication in MVVM?

This question is similar to Where does business logic sit in MVVM?
However, I didn't want to create a comment chain on that one
Lets say for example that I have a table of invoices and I want to get this data and perform some apportionment on it ready for use in 2 totally separate reports and 3 screens.
In our current web application I would have put this in the Data Service Layer, and all of my reports and screens would have called this
In MVVM people seem to suggest that the model should not be bloated out and that logic should be put in the view models. But in this case I be duplicating the code 5 times?
In his answer to my other question Reed states "Anything that's specific to the domain or business should be reusable by other applications, using other architectures."
Can Reed or someone clarify what my approach should be? Can MVVM be combined with other architectures?
I am using Silverlight 5 with the Simple MVVM Toolkit
Paul
The ViewModel is not for business logic. It is for user interface logic. As its name is given, it is representing the View. If you have five different reports that presents similar data, give this data a name and make all five ViewModels understand how to work with the data objects.
Since we are talking about reports, data is understood to e only displayed. The ViewModel can just be a simple data source with minimal user interface interaction, basically a very thin layer.
I faced the same problem and decided to go this way:
I created classes like controllers in MVC (performing some actions with my model) and I work with them in all ViewModels.
For example: our application has a list of books. We need to add/edit/delete them.
So we have a model:
public class Book {
public int BookId { get; set; }
public string Title { get; set; }
public string Author { get; set; }
}
Then we have a controller class:
public class BookController {
string dbPath = ...;
public void AddBook(string title, string author)
{
var book = new Book() { Title = title, Author = author };
AddBook(book);
}
public void DeleteBook(int id)
{
using (var db = new SQLiteConnection(dbPath))
{
db.Delete<Book>(id);
}
}
public void DeleteBook(Book book)
{
using (var db = new SQLiteConnection(dbPath))
{
DeleteBook(book.BookId);
}
}
public List<Book> GetAllBooks()
{
using (var db = new SQLiteConnection(dbPath))
{
return db.Table<Book>().ToList();
}
}
public Book FindBook(string title, string author, int id)
{
.....
}
}
Now we can use it wherever we need, e.g.:
public class BookListViewModel : ViewModelBase {
public BookListViewModel() {
GetData();
}
void GetData()
{
BookController bc = new BookController(); // here we start using our controller.
_books = new List<Book>();
_books = bc.GetAllBooks();
}
}
Such approach helps us:
1) keep all business logic separately (in controller class)
2) avoid code duplication

How to sort views in an ItemsControl in Prism / MEF?

I use prism v4 and MEF to load my modules. My modules contain a handful of views (MVVM) which are loaded in a ItemsControl/NavigationRegion automatically by MEF.
This works nicely, all items show up in the ItemControl. But I don't like the order in which they show. One module might contain several of the items, so changing the module load order is not enough by itself.
How can I sort the different views in the ItemsControl? Is there any way to sort them by some property?
I use prism V4, MEF and exploration due to attributes like in the StockTraderRI example.
This is actually baked into Prism4. Just apply the ViewSortHintAttribute to your views:
[ViewSortHint("100")]
class FirstView : UserControl { }
[ViewSortHint("200")]
class SecondView : UserControl { }
The default sort comparer on the regions will pick up this attribute and sort the views accordingly. You can put any string into the attribute but I tend to use medium sized numbers that allow me to easily put a new view in between existing ones.
Oh dang, this was way easier than I expected:
You can tell the region manager how to sort the views in a specific region. You just need to provide a compare function to the region.
This example sorts by a very stupid value, the function name:
private static int CompareViews(object x, object y)
{
return String.Compare(x.ToString(), y.ToString());
}
this._regionManager.Regions["MyRegion"].SortComparison = CompareViews;
Of course the region needs to be known to the region manager before you can set the SortComparison. So far the only workaround I found to achieve this was to defer to set the comparison function using the Dispatcher:
private readonly IRegionManager _regionManager;
[ImportingConstructor]
public ShellViewModel(IRegionManager regionManager)
{
this._regionManager = regionManager;
Dispatcher dp = Dispatcher.CurrentDispatcher;
dp.BeginInvoke(DispatcherPriority.ApplicationIdle, new ThreadStart(delegate
{
if (this._regionManager.Regions.ContainsRegionWithName("MyRegion"))
this._regionManager.Regions["MyRegion"].SortComparison = CompareViews;
}));
}
Of course one should use some more useful information than the class name for the sorting order, but this should be easy to solve (I'll just add an interface to all views which might be added to this region which provide a value to sort by).
I'm pretty sure you are looking for the CollectionViewSource. Bea provides some information on how to make use of it in the link.
From an MVVM stance this is how I use the ICollectionView within my ViewModel. The _scriptService.Scripts property is an ObservableCollection<T> getting wrapped in an ICollectionView which is returned to the View. The _view.Filter is being used to filter out items within the ICollection, thus changing the View. Similar to typing 'acc' and seeing all items that begin with 'acc' in your list.
public class ScriptRepositoryViewModel : AViewModel
{
private readonly IUnityContainer _container;
private readonly IScriptService _scriptService;
private readonly IEventAggregator _eventAggregator;
private ICollectionView _view;
public ScriptRepositoryViewModel(IUnityContainer container, IScriptService scriptService, IEventAggregator eventAggregator)
{
_container = container;
_scriptService = scriptService;
_eventAggregator = eventAggregator;
}
public ICollectionView Scripts
{
get
{
if (_view == null)
{
_view = CollectionViewSource.GetDefaultView(_scriptService.Scripts);
_view.Filter = Filter;
}
return _view;
}
}
}
Below is the code which takes care of the filtering, and is coming in via a DelegateCommand within Prism, this resides in the same ViewModel.
#region SearchCommand
public DelegateCommand<object> SearchCommand { get; private set; }
private String _search = String.Empty;
private void Search(object commandArg)
{
_search = commandArg as String;
_view.Refresh();
}
public bool Filter(object arg)
{
bool usingPrefix;
IScript script = arg as IScript;
if (script.FileType == ConvertPrefixToFileType(_search, out usingPrefix))
{
if (_search.Length == 2)
return true;
else
return CheckProperties(script, usingPrefix);
}
else
{
if (usingPrefix)
return false;
else
return CheckProperties(script, usingPrefix);
}
}
With the base functionality in place and making use of the ICollectionView you can apply your sorting as follows....
_view.SortDescriptions.Add(new SortDescription("PropertyName", direction));
More information on the sorting behavior can be found here, as there are some performance thoughts to keep in mind.
You could use either metadata or properties. It depends on whether you have control over the interface or not...
Views are displayed in the order they are added:
RegionManager.RegisterViewWithRegion("ListRegion", typeof(ListView));
RegionManager.RegisterViewWithRegion("ListRegion", typeof(ListView2));
RegionManager.RegisterViewWithRegion("ListRegion", typeof(ListView3));
will look like:
----region--|
| view3 |
| view2 |
| view |

Requirements for design-time data source in Report Viewer 2010

What are the requirements for a custom data source to be listed in the 'Data Source' drop-down list when adding a Dataset to a .rdlc report in Report Viewer 2010?
As can been seen from the screen grab, for some reason it is listing potential sources from a variety of referenced assemblies, but I can't see an obvious pattern as to why it is selecting these.
The 'GeneralDataSet' makes sense as that is a strongly-typed Dataset class, but I'm pretty sure most of the others are not, yet the design dialog still lists them.
I'm looking to roll my own custom data source and would prefer it to be selectable from this list.
I think it scans your project file looking for methods that return Lists<> and so on.
So something like:
public class Person
{
public string name { get; set; }
public int age { get; set; }
}
public class GetPeople
{
public List<Person> GetPeopleList()
{
return null;
}
public IEnumerable<Person> GetPeopleIEnumerable()
{
return null;
}
public IQueryable<Person> GetPeopleIQueryable()
{
return null;
}
}
All three show up, so take your pick. (Code is just thrashed out, ignore bad names/practices :))
But when you use a ReportViewer, you will need to manually set the datasets. Selecting it inside the report from what I have found just basically tells it what data to expect. So add an ObjectDataSource or just set it in the code behind.
I noticed the dataset does not appear if the source is exposed as a Property and not a method.
ie this fails to be a selectable data source.
public class FooData
{
public List<string> Data {get;set;}
}
but this will show up as a data source
public class FooData
{
public List<string> GetData();
}
I just had a problem with this also,
my class was returning Lists but would not show up in the datasources list.
I then added a parameterless constructor and it started to show up ( there was not one before ) I assmume this is so the reportviewer can create and instance of it.
eg:
public MyObject()
{
}
I've had a similar problem with custom lists which inherit from List.
You can work around it if your system will allow you to inherit without using interfaces. Ours doesn't.
The project containing this class WILL appear in the DataSource dropdown, and the class itself appears in the DataSet dropdown:
public class AccountList : List<AccountData>
{}
This class will NOT appear as a Dataset, which prevents its project from appearing as a DataSource (notice the "I" in front of AccountData):
public class AccountList : List<IAccountData>
{}
This is a pain because other aspects of our system require the lists to inherit from an interface not a concrete class. I don't know why it doesn't work.

Resources