Separate dataobject variables for controls using MVVM design pattern - c#

Let us say we have a data object which contains two values CompanyID and Price. Also I have more than one control which depending on CompanyID should update Price.
There are four grids with same layout each layout contains a <TextBlock/> control in it. All controls share same variable (Price). If I put <TextBlock Text="{Binding Price}"/> in each of four layouts same value will be updated on every single control. It should update only one <TextBlock/> depending on CompanyID given. Prices will be updated at the runtime.
In modelView constructor:
PriceObj = new Model(CompanyID, Price);
Object will be stored in a property (PriceObj) which is owned by modelView.
What would be the best practice to distinguish which control should update Price value regarding CompanyID value?
Would it be better to create a different Price for each company, let us say Price0, Price1 and then accessing those values directly via DataBinding in XAML?
Can distinguish be made within INotifyPropertyChanged interface implementation?
Note: I do not want the easiest way, I want most MVVM pattern suited way. Code example would be much appreciated.

If you have 100 companies, and each company has its own price, it's very inefficient to produce Price0...Price99 properties. This is not about MVVM, this is about common sense.
Obviously, the way to go is a collection of PriceObj at View Model level, and ItemsControl at View level. Something like this:
public PriceObj
{
public int CompanyID { get; set; }
public decimal Price { get; set; }
}
public class PriceEditorViewModel
{
public ObservableCollection<PriceObj> Prices { ... }
/* ... */
}
XAML:
<ListBox ItemsSource="{Binding Prices}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CompanyID}"/>
<TextBox Text="{Binding Price}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ItemsControl (ListBox in sample) generates a separate set of controls per item in ItemsSource. In the markup above controls are defined using DataTemplate (more about data templating here).

I don't understand your question fully but I guess you have list of companies and their price and you want to show the price when someone selects a company?
If it's the case, I'd create 2 grids, one for the companies, and other for the prices.
I'd also create lists of companies and prices in the view model and whenever someone selects a company, I'd filter price list in view model.
May be I dint understand your qq.

Related

MVVM Binding between two user controls with separate view models

i have a window which loads a Customer table and another user control of input fields, when i select i wish to populate the user control inputs. I currently have a datagrid that a selected item is set in the CustomerViewModel through binding. When this is selected it updates a textbox with the selected items property such as name, email, etc. I have a CustomerSettingsViewModel which contains multiple input fields. I am trying to bind the selected item to inputs within this model, however as the CustomerViewModel doesn't know about the CustomerSettingsViewModel i cant see the binds within the textbox inputs.
The views are loaded using DataTemplate using the datatype.
MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type VM:CustomerVM}">
<View:Customers/>
</DataTemplate>
<DataTemplate DataType="{x:Type VM:CustomerSettingsVM}">
<View:CustomerSettings />
</DataTemplate>
<DataTemplate DataType="{x:Type VM:SuppliersVM}">
<View:Suppliers/>
</DataTemplate>
<DataTemplate DataType="{x:Type VM:SuppliersSettingsVM}">
<View:SupplierSettings/>
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding SelectedMain}" Margin="0,135,0,10" Grid.ColumnSpan="2"/>
<ContentControl Content="{Binding SelectedSettings}" Margin="105,53,10,45"/>
These are set and get the correct views depending on the datatype that being presented.
MainVM.cs
Customer = new CustomerVM();
CustomerSettings = new CustomerSettingsVM();
SelectedMain = Customer;
SelectedSettings = CustomerSettings;
within the CustomerVM i have a get and selected with binds to anything within the customer view, however how can i get the customer settings view to see the selected customer has changed and populate the inputs?
CustomerVM.cs
public Customer SelectedCustomer
{
get { return _selectedCustomer; }
set
{
_selectedCustomer = value;
RaisePropertyChangedEvent("SelectedCustomer");
}
}
i have upload a simple solution of my problem onto GitHub Might give a better understanding of what i am trying to achieve
I think you missed a few concepts about MVVM, maybe you should go back to basics.
Basically, your viewmodels have to be a "testable copy" of your view.
So if your target is to build a Customer View containing Customer Settings, what you need is:
a CustomerViewModel with a CustomerSettingsViewModel property
a CustomerView using CustomerViewModel as datacontext
a CustomerSettingsView declared into your CustomerView binded to the CustomerSettingsViewModel from the CustomerViewModel
Another way to put this: if you want a view to contain another view, you can have a viewModel to contain another viewModel.
This other question could show you how to use a vm as a property of another vm.
Please see my proposed solution using your GitHub example. Basically your problem is that you should not use directly the Customer Model in a View, but create a CustomerVm instead, and just delete the CustomerSettingVm.
You might understand better my implementation by reading how I'm used to deal with MVVM.
Hope it helps.

Loading user controls dynamically based on list of strings (Without code behind)

I am working on a project where the user should be able to export the values of different custom objects.
I am trying to find a way to load a number of check boxes dynamically (i am thinking user controls) based on a list of property names (string). The user should then be able to check or uncheck the check boxes based on the values that should be exported.
The problem I have is that I cannot give the user controls check boxes custom names which would link to the values that should be exported.
ListBox or ItemsControl (if you need more flexibility) are definitely the way to go. However, it's not going to be enough to just generate the CheckBoxes as you're also going to need a way to track whether they're selected, so you want to make some kind of Choice class and bind the properties of the CheckBox to those of your Choice class. Something like this:
<ListBox ItemsSource="{Binding Choices}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}"
IsChecked="{Binding IsChosen}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And the code-behind:
public class Choice
{
public string Name { get; set; }
public bool IsChosen { get; set; }
}
Where your Choices property is a List or IEnumerable of Choices.

An ItemTemplate Containing A Combobox - How Do I Bind To The SelectedValue?

Imagine your working on a UI for an Wedding Planner app. You'll have a list of guests and you want to display their name on the screen next to a combobox containing values of 'Will Attend' / 'Maybe' / 'No'.
I've tried to something just that....I have a collection of items inside a view model (the guests). For each of those items I want to display a Label and a ComboBox. Each ComboBox has the same values in the drop down (the possible responses).
I've created an ItemTemplate that contains a label and a combobox. I bind it to my collection of guests and it works as expected. I'm using ancestor binding so that the ComboBox's ItemsSource is bound to the list of possible responses. That works great.
What I'm struggling with is how to bind the SelectedItem to get the values the user selects? I want to have a collection of selected values on the ViewModel somehow, but I'm having a lot of trouble finding the correct words to describe this / search for it.
Can anyone help me? Am I going about this the wrong way?
You may create an enum for the attendance state and add an Attendance property to your Guest class:
public enum Attendance
{
Yes,
No,
Maybe
}
public class Guest
{
...
public Attendance Attendance { get; set; } // raise PropertyChanged event if necessary
}
Now you could set the Tag property of the ComboBox items to the appropriate enum value and bind the SelectedValue property:
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding Attendance}" SelectedValuePath="Tag">
<TextBlock Tag="Yes">Will Attend</TextBlock>
<TextBlock Tag="No">Won't Attend</TextBlock>
<TextBlock Tag="Maybe">May Attend</TextBlock>
</ComboBox>
</DataTemplate>
</ItemsControl.ItemTemplate>

Best Approach for Stacking an Unknown Number of Rows in WPF

I have a WPF GUI setup like this currently:
The "Check for Third Party Updates" button will query the machine for outdated application installs and display the results, each update grouped in its own row/section with some text describing the update and a button allowing them to initiate the install.
I have a class built for third party updates that contains application name, version, installpath, message to display, etc. My question is largely how to implement the visual components. Every time the list of "apps to be updated" is iterated through and a member is found, a new row needs to be generated with common elements (button, text, picture,etc.). And I don't know how many rows might be generated, so I need to allow for the potential of scrolling down within the tab. Is a listbox control the way to go? How can I setup a visual template for the rows that are dynamically created to adhere to?
A ListBox would be a sensible approach. You would have to create a DataTemplate for the ListBoxItems and assign that to the ItemTemplate property of the ListBox, as described in Styling and Templating an ItemsControl. All the rest, like the ability to select items, or to scroll through the list, is of course done automatically by the ListBox control.
It might look like this:
<ListBox ItemsSource="{Binding ThirdPartyUpdates}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding AppIcon}" Margin="5"/>
<TextBlock Text="{Binding AppName}" Margin="5"/>
<TextBlock Text="{Binding AppVersion}" Margin="5"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The view model for the above ListBox would be something like this:
public class ThirdPartyUpdate
{
public string AppIcon { get; set; }
public string AppName { get; set; }
public string AppVersion { get; set; }
}
public class ViewModel
{
public ObservableCollection<ThirdPartyUpdate> ThirdPartyUpdates { get; set; }
}
You can use ItemsControl and bind it to a collection of your Class and use ItemsControl's template to bind your data to whichever control you want. Check out this Example
For each item in the collection you will have a row created. Surround the item control with a ScrollViewer. Set the VerticalScrollbar visibility to auto so that it will be visible only when required. And if you set a maximumheight to a value you feel right and set the height to auto. It will grow till the maximum height and the scroll bar will be visible if items are added beyond that.

WPF Custom control for receipt preview

Hi,
I have a requirement to show receipt preview as part of WPF page. Sample of receipt is attached.
Each line of text on the receipt can have different alignment(some center, some right or left) and color depending on configuration. Also, the number of lines can vary for each receipt type. I am wondering which controls to be used to effectively implement this. I can create labels dynamically in code behind depending on number of lines and align each one differently with different foreground color but just looking for an effective way if there is any. The width of receipt does NOT vary but length may. Font is same for all lines and all receipt types. Any ideas are really appreciated.
Thanks
It is normally better to avoid dynamically adding controls like labels or textblocks from your code behind. This type of code is difficult to read and almost impossible to test. Instead, you should use a view-model class (look up the MVVM pattern). Your view-model could have a property returning a list of ReceiptItem and then in your view (the XAML file) you make an ItemsControl and bind it to your list of ReceiptItems. Now you can create a template for the ReceiptItem class so that they show up a desired using Label, TextBlock, or whatever you decide is appropriate.
For example, in C# you would need two classes:
public class MyReceiptViewModel
{
public List<ReceiptItem> ReceiptItems { get; set; }
}
public class ReceiptItem
{
public string Content { get; set; }
public bool IsHighlighted { get; set; }
}
Your view might look like (this assumes that you have an instance of MyReceiptViewModel as your data context):
<ItemsControl ItemsSource="{Binding ReceiptItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Content}"
Foreground="{Binding IsHighlighted, Converter={StaticResource MyColorFromBooleanConverter}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Categories