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>
Related
I have a WPF application made for an LCD display with a specific resolution. The WPF application is fixed size. The view is bound to its view model by DataTemplates like this:
<DataTemplate DataType="{x:Type vm:IdleViewModel}">
<v:IdleView/>
</DataTemplate>
Now I would like to make this application available for a second LCD display type. The view will be totally different. Is there any way to bind the view model to a different view depending the resolution?
There is more than one way to do this depending on your requirements. Let me focus on two different approaches that might work for you. How to find out the screen resolution is another topic, see:
How to get the size of the current screen in WPF?
Data Template Selector
You can create a data template selector that returns a DataTemplate based on screen resolution. The mechanism to determine the screen resolution is take from the question above.
public class ResolutionDependentTemplateSelector : DataTemplateSelector
{
public DataTemplate InvalidResolutionTemplate { get; set; }
public DataTemplate Resolution1Template { get; set; }
public DataTemplate Resolution2Template { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (IsTargetResolution(480, 576))
return Resolution1Template;
if (IsTargetResolution(720, 480))
return Resolution2Template;
return InvalidResolutionTemplate;
}
private bool IsTargetResolution(double width, double height)
{
return Math.Abs(SystemParameters.PrimaryScreenWidth - width) < 1 &&
Math.Abs(SystemParameters.PrimaryScreenHeight - height) < 1;
}
}
You can assign this selector in XAML. Since I do not know which control you are using, this example uses a simple ContentControl. The property to assign it to may vary.
<ContentControl>
<ContentControl.Resources>
<DataTemplate x:Key="Resolution1Template">
<!-- ...your markup. -->
</DataTemplate>
<DataTemplate x:Key="Resolution2Template">
<!-- ...your markup. -->
</DataTemplate>
</ContentControl.Resources>
<ContentControl.ContentTemplateSelector>
<local:ResolutionDependentTemplateSelector Resolution1Template="{StaticResource Resolution1Template}"
Resolution2Template="{StaticResource Resolution2Template}"/>
</ContentControl.ContentTemplateSelector>
</ContentControl>
Resource Dictionaries
You could create separate resource dictionaries that contain the data template for each different screen along with other specific resources that only apply to a certain screen size. Then on startup (e.g. in the Apps OnStartup method), merge the resource dictionary that fits the screen resolution into the application resources (App.Resources).
Depending on the size and complexity of your application you could separate the controls for each distinct screen resolution into its own project, similar to the resource dictionary approach. If the application is always run on a specific LCD type and never on another, it would also be possible to create targets for each, so each "platform" contains only the resources it needs.
Is there any way to bind the view model to a different view depending the resolution?
Not using a single view model type and a corresponding DataTemplate alone.
You could either
Use two different view model types and views
Implement the view to adopt itself according to the view model (which should then know about the current screen resolution)
Use a DataTemplateSelector to select the appropriate view based on some logic other than just the type of the view model
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.
Let me state problem first. I would like to implement wrapper around Canvas (let me call it Page) which would implement selecting rectangle around its UIElements which are actually selected.
For this I implemented ISelect interface like so :
interface ISelect {
Point Center {get; set;} //Center of selecting rectangle
Size Dimensions {get; set;} //Dimensions of selecting rectangle
}
Every object that is put to Page implements ISelect interface.
Page has SelectedElements of type ObservableCollection which holds reference to all currently selected elements.
For every entry in SelectedElements i would like to draw rectangle around it.
I have few ideas how to do this :
Every UIElement can implement on its own this rectangle and show it when selected. This option would require for new objects to implement this every time. So I rather not use it.
In Page I could create rectangles in code-behind in add them to the Page. It isn't MVVM recommended priniciple.
In Page XAML create somehind like ItemsControl and bind it to SelectedElements with specific template. This option seems like the best one to me. Please help me in this direction. Should I somehow use ItemsControl?
Thank you.
I don't have time to dig a complete working solution, so this is mostly a collection of suggestions.
Each element should have view model
public abstract class Element: INotifyPropertyChanged
{
bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
OnPropertyChanged();
}
}
}
public class EllipseElement : Element {}
public class RectangleElement : Element {}
Then there are data templates to visualize elements (I can't give you converter code, but you can replace it with another, look here).
<DataTemplate DataType="{x:Type local:EllipseElement}">
<Border Visibility="{Binding IsSelected, Converter={local:FalseToHiddenConverter}}">
<Ellipse ... />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type local:RectangleElement}">
<Border Visibility="{Binding IsSelected, Converter={local:FalseToHiddenConverter}}">
<Rectangle ... />
</Border>
</DataTemplate>
Then bind ObservableCollection of elements to canvas (which is tricky, see this answer, where ItemsControl is used to support binding).
Your selection routine has to hit-test elements and set/reset their IsSelected property, which will show border. See here regarding how to draw over-all selection rectangle.
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.
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.