Best Approach for Stacking an Unknown Number of Rows in WPF - c#

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.

Related

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.

UI Implementation, best route to take

I'm working on a small application that I need some assistance with implementing.
The gist is I would like to create a grid-like container that houses a dynamic number of the identical column-like structures. Inside each column-like structure is a few text fields and radio buttons that the user can interact with.
I've been looking into some different WPF objects that may be of some help, but I'm pretty overwhelmed.
Some things I've thought of:
The column-like structure can be a custom built UserControl. This UserControl will have all the logic to deal with the interactions of the various buttons and text fields.
We can use a StackPanel, set to Horizontal, to house these UserControls. From what I've gathered, a StackPanel seems like it may be the perfect container for my purpose.
Some questions I have:
Will I need to create a .xaml for the UserControl?
In the event that more UserControls that can be displayed are added, does the StackPanel provide a way to scroll from left to right with a horizontal scroll bar?
Do I need to custom define the size of UserControl, or is it possible to just specify a set width and use the height of the StackPanel?
Is there an easier or more appropriate solution?
Lastly, I've included a very rough sketch to provide a visual idea of what I'm looking to do:
I will try to answer your questions:
You shall definitely extract a control for each part of your control which is used more than once.
A stack panel is a container, is similar to a div in HTML. If you want to show a scroll, you better use ScrollViewer instead of Stackpanel.
If the height of the StackPanel/ScrollViewer is set, controls inside them, will respect that limit unless you actually set an explicit height for the child elements. If the height of the Stackpanel/ScrollViewer is not set, the children elements will be stretched (so, in order to fit into the parent, you have to specify a height). You can decide, however, how you want to do it.
I think that is a god solution to extract a control for each redundant part and use a ScrollViewer!
If something is unclear, please let me know! Good luck :)
I would start by reading a bit about the MVVM pattern which plays well with WPF. Other than that I would use a ListView instead of a horizontal stackpanel and a scrollviewer. I Would prefer my data to expand vertically than horizontally. Here's some sample code to use.
Here's your model class:
public enum CustomOption
{
Option1,
Option2,
Option3
}
public class Item
{
public string Value1 { get; set; }
public string Value2 { get; set; }
public string Value3 { get; set; }
CustomOption Option { get; set; }
}
Here's your ViewModel:
public class MainWindowVM
{
public ObservableCollection<Item> Items { get; set; }
}
And here's your MainWindow:
<ScrollViewer
<ListView ItemsSource="{Binding Items}">
<ListView.Resources>
<ObjectDataProvider x:Key="EnumValues" MethodName="GetValues" ObjectType="{x:Type System:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:CustomOption"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Value1" Margin="5"/>
<TextBox Text="{Binding Value1}" Margin="2"/>
<TextBlock Text="Value2" Margin="5"/>
<TextBox Text="{Binding Value2}" Margin="2"/>
<TextBlock Text="Value3" Margin="5"/>
<TextBox Text="{Binding Value3}" Margin="2"/>
<TextBlock Text="Option" Margin="5"/>
<ComboBox ItemsSource="{Binding Source ={StaticResource EnumValues}}" SelectedItem="{Binding Option}" Margin="5"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
This is a very simplistic example to get you started. You can play around to get a much better looking style and layout. I used a ComboBox instead of RadioButtons which I dislike. You also need to set the DataContext of your MainWindow as the MainWindowVM somewhere. You can do that when your application starts for now.

TwoWay Binding Not Updating Target - Metro App

I'm building a Metro App using VS 2012 and the Windows 8 SDK. In the app, I have this class (with a corresponding struct)
// Parameter data structure for tools
public struct ToolParameter
{
public string Title { get; set; }
public Object Value { get; set; }
public string Description { get; set; }
}
// Tool that will be used to execute something on phone
public class Tool
{
public string Title{ get; set; }
public ObservableCollection<ToolParameter> Parameters { get; set; }
public string Description { get; set; }
}
On a certain page in the app, I bind an instance of the class to the dataContext of the page
this.DataContext = currentTool;
On the page, I display various information about the app, including the parameters, which I want to make editable on the page. Because of this, I'm using a TextBox to display the parameters so that it can be edited, and binding it to the "Value" member of the ToolParameter struct.
<TextBox x:Name="ParameterValue" FontSize="15" Text="{Binding Value, Mode=TwoWay}" TextWrapping="Wrap"/>
Unfortunately, when a TextBox is bound to a value, it doesn't update until it no longer has a focus, so I added a button that the user can click that will update the parameters (and change focus from the TextBox). Unfortunately, upon clicking of the button, though the focus changes, the values of the parameter in the currentTool variable is never changed. Is there something about data binding that I am missing? Might it be that the parent of the TextBox named ParameterValue (the parameters are all part of a ListView) has to be two way as well?
From what I can see, youre TextBox is binding to Value which is a property of the ToolParameter class. The DataContext for the page is of type Tool. Tool contains Parameters which is a collection of ToolParameter objects. So, the TextBox needs to be within an ItemsCollection that has the ItemsSource set to bind to the Parameters property.
Example:
<StackPanel>
<TextBlock Text="{Binding Title}"/>
<TextBlock Text="{Binding Description}"/>
<!-- showing a ListBox, but can be any ItemsControl -->
<ListBox ItemsSource="{Binding Parameters}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
Also make sure that your classes Tool and ToolParameter implement INotifyPropertyChanged and that the setter for your properties fire the PropertyChanged event
UPDATE: Adding info that was too large for a comment
This should help understand Source/Target in bindings. For your TextBox, the source of the binding is the Value property and the Target is the TextProperty of the TextBox. When the source updates, the Text will update within the TextBox. If you the TextProperty of the TextBox changes, then it will update the Value property of your object (provided mode is set to TwoWay). You're tool however will NOT update and neither will the Parameters property of the Tool class. If you wish to update the tool object when a property of a ToolParameter updates, then you will need to subscribe to the PropertyChanged event of each ToolParameter object that gets added to the Parameters collection.
Welcome to StackOverflow!
In the binding, you can specify the UpdateSourceTrigger to 'PropertyChanged':
<TextBox Text="{Binding Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
The default value, which you are experiencing, is 'LostFocus'.

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>

Editable WPF ListBox

I have a ObservableCollection that's bound to a ListBox in WPF. I want the ListBox to be editable, and for the editing changes to be saved to the collection. Since WPF doesnt provide an editable listbox, I've tried creating my own by changing the ListBox.ItemTemplate.
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="EditableText" Text="{TemplateBinding Content}"/>
</DataTemplate>
</ListBox.ItemTemplate>
Changing the ItemTemplate gives me editable boxes, but any changes to the textboxes dont get saved to the ObservableCollection. Is there a way to have an editable ListBox with two way binding?
You cannot do it this way.
To achieve that kind of trick, you would need your items to be "holder classes" that expose a property you can bind your textbox to.
To understand it, imagine the following pseudo sequence of calls:
class ListBox
{
Bind(Items)
{
foreach(var item in Items)
{
DataTemplate Template = LoadTemplateForItem(item.GetType()); // this is where your template get loaded
Template.Bind(item); //this is where your template gets bound
}
}
}
Your template (the DataTemplate with the listbox) is loaded and the item (which I assume is a string in your case) gets passed in.
At this point, it only knows the string, and cannot influence anything upwards. A two-way binding cannot influence the collection because the template does not know in which context it is being used, so it cannot reach back to the original collection and modify its contents.
For that matter, this is the same thing for the TextBox. If it is not given a conainer and a property name, it has nowhere to "store back" the changes.
This basically the same as passing a string into a function call. The function cannot change which string was passed in (ignoring tricks such as by-reference argument passing).
To get back to your case, you need to build a collection of objects which expose a property containing the value that needs to be edited:
public class MyDataItem
{
string Data { get; set;}
}
Then you can bind your ListBox to a collection of those items and modifiy your datatemplate:
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="EditableText" Text="{Binding Data, Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
Bind to a model property -- i.e. a property of the data object -- rather than to a view property such as Content. For example:
// model class
public class Widget : INotifyPropertyChanged
{
public string Description { ... }
}
<!-- view -->
<DataTemplate>
<TextBox Text="{Binding Description}" />
</DataTemplate>
Note this will not work if your ItemsSource is ObservableCollection (because there's no property to bind to).

Categories