Computing and viewing column outside model using MVVM - c#

I need to solve following problem using C# MVVM. I'm using following model.
And one of my UserControls got following ListBox template.
<ListBox ItemsSource="{Binding OrdersListViewViewModel.AllItems, Source={StaticResource Locator}}" SelectedItem="{Binding OrdersListViewViewModel.SelectedItem, Source={StaticResource Locator}}" Background="White">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" Margin="0 0 0 0" Height="Auto" Width="Auto" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,7,0,6" HorizontalAlignment="Left" Orientation="Horizontal">
<Image Width="25" Height="25" Margin="5 2 0 0" Source="{Binding OrdersListViewViewModel.OrderDeliveryStateImage, Mode=OneWay, Source={StaticResource Locator}}"/>
<TextBlock Margin="25,5,25,5" Text="{Binding OrdersListViewViewModel.AllItems/Customer.CustomerName, FallbackValue=N/A, Mode=OneWay, Source={StaticResource Locator}}" FontSize="20"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The list is showing all orders (customer names) loaded from database. Image which is beside the TextBlock I want to fill in with a picture. If all orderitems under the order have been delivered (Delivered=1) it should use picture1, otherwise picture2.
So I'm binding ObservableCollection<Order>. Model is generated from .tt using entity framework (db first) so it's bad idea to place computing directly into Order.cs class because of possible db updates.
My first idea was to use MSSQL Computed column but I think that's not a good aproach (there can be a lot of situations like this one in a solution) so model would be huge and complicated.
Second idea was to use a converter but it should be used for a simple tasks, not for a computation logic (which this is).
Third idea was to change ObservableCollection<Order> to ObservableCollection<Tuple<string,Order>> and somehow bind it to a view but....you know, that's a bad idea.
So my question is simple. How can I solve this issue (where to place a computation logic of this purpose) using a MVVM best practice.
Thanks.

Well after a googling around I've decided to create this solution. Maybe it will help to someone.
First I've created partial class of Order entity in order to separate files so when entity framework will update the Order entity, it will not overwrite my customizations.
Then I've created custom property which determine if the Order has been delivered. So computation logic still stays at the model.
public partial class Order
{
public bool IsOrderDelivered
{
get
{
int orderDelivered = 1;
foreach (var orderItem in this.OrderItems)
{
orderDelivered = orderDelivered * orderItem.Delivered;
}
return orderDelivered == 1 ? true : false;
}
}
}
Then I've created converter which just converts boolean to text which is why it exists and it's used the right way.
public class OrderStatusToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value == true ? #"Skins\approved.png" : #"Skins\denied.png";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And created databinding on my new property in partial class together with converter.
<StackPanel Margin="0,7,0,6" HorizontalAlignment="Left" Orientation="Horizontal">
<Image Width="25" Height="25" Margin="5 2 0 0" Source="{Binding IsOrderDelivered, Converter={StaticResource OrderStatusToImageConverter}, Mode=OneWay}"/>
<TextBlock Margin="25,5,25,5" Text="{Binding Customer.CustomerName, FallbackValue=N/A, Mode=OneWay}" FontSize="20"/>
</StackPanel>
And finally the visual effect.

Related

Combining three slider values into one Texblock Text / Label Content?

I'm currently working on a User Interface in which the user can select three values via three separate sliders. I've got three textblocks in front of the sliders to indicate the current value of the specific slider:
In the .xaml:
<Label Content="Sample Selection" FontSize="16" FontStyle="Italic" FontWeight="Bold" HorizontalAlignment="Left" Margin="0,-30,0,0" VerticalAlignment="Top"/>
<Label Content="Patient Samples (max 64)" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" FontStyle="Italic"/>
<Slider x:Name="SampleAmountSlider" HorizontalAlignment="Left" Margin="181,14,0,0" VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="64" ValueChanged="SampleAmountSlider_ValueChanged" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="SampleSliderValue" HorizontalAlignment="Left" Margin="165,16,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top"/>
<Label Content="Calibrators (max 7)" HorizontalAlignment="Left" Margin="10,36,0,0" VerticalAlignment="Top" FontStyle="Italic"/>
<Slider x:Name="CalAmountSlider" HorizontalAlignment="Left" Margin="181,40,0,0" VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="7" ValueChanged="CalAmountSlider_ValueChanged" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="CalSliderValue" HorizontalAlignment="Left" Margin="165,42,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top"/>
<Label Content="Control Samples (max 4)" HorizontalAlignment="Left" Margin="10,62,0,0" VerticalAlignment="Top" FontStyle="Italic"/>
<Slider x:Name="ControlAmountSlider" HorizontalAlignment="Left" Margin="181,66,0,0" VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="4" ValueChanged="ControlAmountSlider_ValueChanged" IsSnapToTickEnabled="True"/>
<TextBlock x:Name="ControlSliderValue" HorizontalAlignment="Left" Margin="165,68,0,0" TextWrapping="Wrap" Text="0" VerticalAlignment="Top"/>
<Label Content="Total Sample Preparations Selected:" HorizontalAlignment="Left" Margin="10,105,0,0" VerticalAlignment="Top" FontWeight="Bold" FontStyle="Italic"/>
<TextBlock x:Name="TotalPrepValue" HorizontalAlignment="Left" Margin="225,110,0,0" FontWeight="Bold" FontStyle="Italic" Text="0" VerticalAlignment="Top"/>
In .xaml.cs:
private void SampleAmountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
SampleSliderValue.Text = Math.Round(e.NewValue, 0).ToString();
}
private void CalAmountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
CalSliderValue.Text = Math.Round(e.NewValue, 0).ToString();
}
private void ControlAmountSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
ControlSliderValue.Text = Math.Round(e.NewValue, 0).ToString();
}
What I want is the last text block (named TotalPrepValue) to contain a sum of the amount of patient samples, amount of calibrators and amount of control samples.
I'm not sure if I'm asking a lot or things are unclear (if so, please let me know. I'll answer as fast as possible).
The thing is that I'm a very inexperienced programmer, yet willing to learn!
Thank you in advance for your help!
Personally, this is what I would do. However if you like it than please go also upvote #JerryNixon 's answer here since it's basically just a re-factoring to use sliders instead of his example and he deserves more praise. Normally I'd have just pointed you to it directly but I know how it is when you're just getting started with something and a clearer PoC can be more useful.
Anyway, here ya go, a pretty picture to start...
The XAML;
<Window.Resources>
<local:SumConverter x:Key="MySumConverter" />
</Window.Resources>
<Grid>
<StackPanel VerticalAlignment="Center">
<StackPanel.Resources>
<Style TargetType="Slider">
<Setter Property="Margin" Value="10"/>
<Setter Property="Width" Value="200"/>
<Setter Property="Minimum" Value="0"/>
<Setter Property="Maximum" Value="100"/>
</Style>
</StackPanel.Resources>
<Slider x:Name="Slider1"></Slider>
<Slider x:Name="Slider2"></Slider>
<Slider x:Name="Slider3"></Slider>
<TextBlock HorizontalAlignment="Center" TextAlignment="Center">
<Run Text="{Binding Value, ElementName=Slider1}"/>
<LineBreak/><LineBreak/>
<Run Text="{Binding Value, ElementName=Slider2}"/>
<LineBreak/><LineBreak/>
<Run Text="{Binding Value, ElementName=Slider3}"/>
<LineBreak/>
<Run Text="______________________"/>
<LineBreak/><LineBreak/>
<Run>
<Run.Text>
<MultiBinding Converter="{StaticResource MySumConverter}"
StringFormat="{}{0:C}"
FallbackValue="Error" TargetNullValue="Null">
<Binding Path="Value" ElementName="Slider1"/>
<Binding Path="Value" ElementName="Slider2"/>
<Binding Path="Value" ElementName="Slider3"/>
</MultiBinding>
</Run.Text>
</Run>
</TextBlock>
</StackPanel>
</Grid>
The Converter;
public class SumConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
double _Sum = 0;
if (values == null)
return _Sum;
foreach (var item in values)
{
double _Value;
if (double.TryParse(item.ToString(), out _Value))
_Sum += _Value;
}
return _Sum;
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Hope this helps, cheers.
You can, as others have suggested, use IMultiValueConverter in this scenario. But it's my opinion that, while a useful tool in other scenarios, that's the wrong tool for the job here. The reason being that in this case, it would be used to perpetuate the inappropriate use of UI elements as a place to store your non-UI data.
You will be much better served, while writing WPF programs, if you commit yourself to following the MVVM-style of programming WPF was intended to be used with. The term "MVVM" means literally "model, view, view-model". From that point of view, there will always be special-purpose "adapter" types between the model and the view. But it's been my experience that the important part of the MVVM paradigm is to be strict about keeping view logic separate from model logic, and this can often be done without the extra layer of the "view model" types. This puts MVVM in the same set of tools as MVC ("model, view, controller") and MVP ("model, view, presenter").
The key to all of these is that you have some business logic which is represented in model data structures, implemented by types that provide some form of value-changed notification (in WPF, the primary mechanism here is INotifyPropertyChanged), and then also view logic which is represented completely separately (in WPF, the view is mostly, and in many cases entirely, declared in XAML).
In your example, this means we'd want a model data structure that represents the data you are interested in: the sample, calibrator, control, and total prep counts. The last one being simply the sum of the first three. As long as we have a class that can keep track of these, and correctly update the summed value when any of the other three change, we can bind this directly to a view declared in XAML, without the use of any C# code-behind at all.
For example:
class ViewModel : INotifyPropertyChanged
{
private int _sampleCount;
public int SampleCount
{
get { return _sampleCount; }
set { _UpdateField(ref _sampleCount, value, OnCountChanged); }
}
private int _calibratorCount;
public int CalibratorCount
{
get { return _calibratorCount; }
set { _UpdateField(ref _calibratorCount, value, OnCountChanged); }
}
private int _controlCount;
public int ControlCount
{
get { return _controlCount; }
set { _UpdateField(ref _controlCount, value, OnCountChanged); }
}
private int _totalPrepCount;
public int TotalPrepCount
{
get { return _totalPrepCount; }
set { _UpdateField(ref _totalPrepCount, value); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnCountChanged(int previousValue)
{
TotalPrepCount = SampleCount + CalibratorCount + ControlCount;
}
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Notes:
The above class has four properties, one property for each of the values we want to keep track of.
Three of the properties are just simple value containers. There is a callback method that is called whenever any of those are modified, and in that method the code simply sets the fourth property to the sum of the three.
The INotifyPropertyChanged interface has just a single member, the PropertyChanged event. You will find when dealing with MVVM-style code, it's helpful to have a base class that actually implements this event, and a helper method like the _UpdateField() method shown above, which property setters can call to handle the repetitive logic needed for each such property. In the example above, I've combined all of this logic into a single class for the sake of simplifying the example, but you'll probably want to keep a suitable base class around (I and many other people have configured snippets in Visual Studio to easily insert this boilerplate code into a project).
With a view model so-defined, the XAML is simplified to look something like this:
<Window x:Class="TestSO45170241SliderExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO45170241SliderExample"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:ViewModel/>
</Window.DataContext>
<Grid>
<Label Content="Sample Selection" FontSize="16" FontStyle="Italic" FontWeight="Bold"
HorizontalAlignment="Left" Margin="0,-30,0,0" VerticalAlignment="Top"/>
<Label Content="Patient Samples (max 64)" HorizontalAlignment="Left" Margin="10,10,0,0"
VerticalAlignment="Top" FontStyle="Italic"/>
<Slider HorizontalAlignment="Left" Margin="181,14,0,0"
VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="64"
Value="{Binding SampleCount}" IsSnapToTickEnabled="True"/>
<TextBlock HorizontalAlignment="Left" Margin="165,16,0,0"
TextWrapping="Wrap" Text="{Binding SampleCount}" VerticalAlignment="Top"/>
<Label Content="Calibrators (max 7)" HorizontalAlignment="Left" Margin="10,36,0,0"
VerticalAlignment="Top" FontStyle="Italic"/>
<Slider HorizontalAlignment="Left" Margin="181,40,0,0"
VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="7"
Value="{Binding CalibratorCount}" IsSnapToTickEnabled="True"/>
<TextBlock HorizontalAlignment="Left" Margin="165,42,0,0"
TextWrapping="Wrap" Text="{Binding CalibratorCount}" VerticalAlignment="Top"/>
<Label Content="Control Samples (max 4)" HorizontalAlignment="Left" Margin="10,62,0,0"
VerticalAlignment="Top" FontStyle="Italic"/>
<Slider HorizontalAlignment="Left" Margin="181,66,0,0"
VerticalAlignment="Top" Cursor="Hand" Width="160" Maximum="4"
Value="{Binding ControlCount}" IsSnapToTickEnabled="True"/>
<TextBlock HorizontalAlignment="Left" Margin="165,68,0,0"
TextWrapping="Wrap" Text="{Binding ControlCount}" VerticalAlignment="Top"/>
<Label Content="Total Sample Preparations Selected:" HorizontalAlignment="Left"
Margin="10,105,0,0" VerticalAlignment="Top" FontWeight="Bold" FontStyle="Italic"/>
<TextBlock HorizontalAlignment="Left" Margin="225,110,0,0"
FontWeight="Bold" FontStyle="Italic" Text="{Binding TotalPrepCount}"
VerticalAlignment="Top"/>
</Grid>
</Window>
(Aside: other than changing to support the MVVM approach, I did not modify your basic UI declarations at all. I do agree that another thing you'll want to start gaining familiarity with is how to take advantage of WPF's various layout containers and element styling features. But I think that introducing those here would just confuse matters. By keeping your original UI mostly intact, you can focus on just those things that are different from what you originally had, helping you understand better the data binding aspect without distraction.)
In this implementation, there is no code whatsoever added to the MainWindow.xaml.cs file. All that's in that file is the default call to InitializeComponent() in the constructor, provided by Visual Studio's template for WPF projects.
In the XAML, on the other hand, I've replaced the event handler subscriptions with bindings straight to the Slider.Value properties. Note also that the TextBlock.Text properties are also bound to the same properties. In this way, WPF does all the heavy lifting of storing slider values in a business-logic-only data structure, as well as of then redisplaying those values in text fields in the view. You'll note that WPF even handles conversion between the various data types: the view model stores int values, but the sliders use double and the text blocks of course use string.
And of course, the TotalPrepCount field is bound to the TextBlock.Text property of interest for display as well.
Finally, I'll note that even in your simple example, you have additional places where one might want to apply this data binding approach. In particular, your sliders each have maximum values, which are hard-coded into the view. The point of MVVM is for the view to not have to encapsulate any knowledge about the business logic. This would include not having to know the full range of values permissible (*).
So, your view model could also have e.g. a MaxSampleCount property, which is bound both to the Slider.Maximum property, and to the Label.Content property. In the latter case, you can use the Binding.StringFormat property to have the value incorporated into text as appropriate. For example:
<Label Content="{Binding MaxSampleCount, StringFormat=Patient Samples (max {0})" ... />
I will readily admit that when I first started trying to use WPF, after years and years of using UI APIs like the native Win32 controls, MFC, Windows Forms, Java's APIs (Swing, AWT, SWT), and even Mac OS's Cocoa framework (which uses a form of data binding, but not the declarative UI like XAML), I struggled to change my thinking away from the procedural approach used in all those other APIs to get used to the mixed declarative and procedural approach used with WPF.
But I was trying to learn it strictly from the documentation provided by MSDN, which is densely written at best, most often just plain difficult to follow, and in many cases, completely useless. If someone had just shown me an example like what I've shown above (and they did exist, even back then…I just didn't know where they were), I would have seen back then just how easy the basic MVVM approach is, and how much more quickly one can write a WPF program if one follows that approach.
I hope the above helps you get on track for productive use of WPF, and shows you the basics in an easy-to-understand way.

Datagrid ObservableCollection for one row wpf

I want to populate a datagrid with itemsource={Binding Model}.
This is not working out. It seems as the datagrid does not understand how to display these properties.
An easy but silly workaround works great:
In viewmodel:
Props= new ObservableCollection<MonitoringBinaryModel>();
_Model = new MonitoringBinaryModel(name);
Props.Add(_Model);
Then in xaml
itemsource={Binding Props}
Seems silly to create an observablecollection when its only suppose to hold one item. Is there a better way to make any type of instance observable?
DataGrid is designed to display a collection of objects of same type. Collection is a must. If you want DataGrid to show a content of your model, you need to obey former's design, by either using ObservableCollection or implementing a bunch of interfaces which would allow your viewmodel's properties to be retrieved in 'collection way'.
I used to have a bunch of models implementing ITypedList interface back in Windows Forms time - it wasn't a simple exercise to say the truth, so if I were you I'd rather go for either way:
Wrap model into any collection - exactly as you've stated
Replace data grid with container grid plus a number of direct bindings, like this:
<Grid>
<TextBlock Grid.Column="2" Grid.Row="0" Text="Prop1"/>
...
<TextBlock Grid.Column="2" Grid.Row="1" Text="{Binding Prop1}"/>
Well ItemsSource property is of type IEnumarable so until your MonitoringBinaryModel implement IEnumerable binding will not work.
Again because ItemsSource is IEnumerable you should provide IEnumerable as binding source. So there is no need to make yout Props as ObservableCollection. You can use ordinary List<>, or anything implementing IEnumerable with your single MonitoringBinaryModel:
_Model = new MonitoringBinaryModel(name);
Props = new List<MonitoringBinaryModel> { _Model };
Other option is to use CompositeCollection inside your XAML:
<DataGrid.ItemsSource>
<CompositeCollection>
<Binding Path="_Model"/>
</CompositeCollection>
</DataGrid.ItemsSource>
reusable wrapper via converter:
public class ItemsSourceConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// doesn't allow to add new rows in DataGrid
return Enumerable.Repeat(value, 1);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
usage in xaml
add converter to resourses:
<Window.Resources>
<wpfApplication1:ItemsSourceConverter x:Key="ItemWrapper"/>
</Window.Resources>
and use converter resourse with binding
<DataGrid ItemsSource="{Binding Path=Model, Converter={StaticResource ItemWrapper}}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Name}"/>
</DataGrid.Columns>
</DataGrid>
or
<ItemsControl ItemsSource="{Binding Path=Model, Converter={StaticResource ItemWrapper}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

C# WPF: Listbox with drag to select text

I need to create a WPF ListBox that supports two features:
Content Converter Binding:
The items in the ListBox need to be passed to a converter that converts the items to a text format.
Display items in a way that lets users select and copy text from ListBox items
I need the text of each ListBox item to be selectable. Users want to use their mouse to drag-to-select parts of the elements so they can copy the text to their clipboard.
I implemented [this copy/paste solution][1] but it does not let a user select parts of the ListBox item text, rather it supports copying the entire text.
I'm able to create a ListBox using the converter, but I can not figure out how to put the converted text into a control that lets users select the displayed text. Here is what I have:
<ListBox Name="FinishedTestErrorsListBox"
FontSize="12"
ItemsSource="{Binding Path=SelectedComparisonResult.TestFailItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding Converter={StaticResource testFailItemConverter}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I've tried adding a TextBox to the DataTemplate as shown below...
<TextBlock Text="{Binding Converter={StaticResource testFailItemConverter}}"/>
... but this creates a runtime error caused by sending the wrong type of object to the converter. I know here I'm not setting up the converter binding properly, though I don't have a good grasp on how I should setup the binding here or why this causes errors.
So, my question is:
What content container can I use to let users select text from the individual ListBox items?
Thank you for any help,
Charlie
EDIT
Here's the converter code...
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ITestFailItem i = (ITestFailItem)value;
return i.Itemize();
}
EDIT 2
The following runtime error is throw when the ListBox is first initialized:
An unhandled exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll
Additional information: Provide value on 'System.Windows.Baml2006.TypeConverterMarkupExtension' threw an exception
EDIT 3
The culprit is a line of code I'd omitted from the original snippet as I thought it was irrelevant - I've learned a good lesson along the way!
Extension Question
Why does the following snippet cause an error? How can I achieve the desired affect of making the textbox span the entire containing grid?
<TextBox Width="*"
Text="{Binding Path=., Converter={StaticResource testFailItemConverter}}"/>
Try this. TextBlocks don't support text selection, but TextBoxes do. You just have to make it read-only so the user can't modify the text, and change its border thickness and background so they look like labels:
<ListBox Name="FinishedTestErrorsListBox"
FontSize="12"
ItemsSource="{Binding Path=SelectedComparisonResult.TestFailItems}">
<ListBox.Resources>
<converter:TestFailItemConverter x:Key="testFailItemConverter" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=.,
Converter={StaticResource testFailItemConverter},
Mode=OneWay}"
BorderThickness="0"
Background="Transparent"
IsReadOnly="True"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Have You tried TextBox? You can select text inside textbox. Path have to be changed to Path=.
<TextBox Text="{Binding Path=., Converter={StaticResource testFailItemConverter}}" />
There is not much code to work with, but this code works for me:
xaml:
<Window x:Class="StackOverflowTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:StackOverflowTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<s:TestFailItemConverter x:Key="testFailItemConverter" />
</Window.Resources>
<Grid>
<ListBox Name="FinishedTestErrorsListBox"
FontSize="12"
ItemsSource="{Binding Path=SelectedComparisonResult.TestFailItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<!--<ContentControl Content="{Binding Converter={StaticResource testFailItemConverter}}"/>-->
<TextBox Text="{Binding Path=., Converter={StaticResource testFailItemConverter}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Model's code:
public class Dummy
{
public ObservableCollection<string> TestFailItems { get; set; }
public Dummy()
{
TestFailItems = new ObservableCollection<string>(new List<string> { "a", "b" });
}
}
public class Model
{
public Dummy SelectedComparisonResult { get; set; }
public Model()
{
SelectedComparisonResult = new Dummy();
}
}
Converter's code:
public class TestFailItemConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return "aa";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}

Binding to instance of ItemsSource

I have a ListBox on a WPF User Control that is defined as
<ListBox Grid.Row="1" ScrollViewer.CanContentScroll="False" Background="#00000000" BorderThickness="0" ItemsSource="{Binding BuyItNowOptions}"
ItemTemplate="{DynamicResource BuyItNowDataTemplate}" IsSynchronizedWithCurrentItem="True"
Style="{DynamicResource InheritEmptyListStyle}" SelectedItem="{Binding SelectedResearch}" ItemContainerStyle="{DynamicResource ListBoxItemStyle}"/>
BuyItNowOptions is a public property on the ViewModel that is of type ObservableCollection
In the BuyItNowDataTemplate I have a label that needs to have some logic performed before displaying a price.
<Label Padding="1" HorizontalContentAlignment="Stretch" Grid.Column="2" Grid.Row="2" Margin="1">
<TextBlock Text="{Binding ExchangePrice, StringFormat=C}"
Visibility="{Binding ReturnRequired, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Label>
The binding here indicates that it will use the ExchangePrice property of the instance of AutoResearchProxy that it is on like BuyItNowOptions[CurrentIndex].ExchangePrice.
What I would like to know is it possible to create the binding in such a way that it references the whole instance of the AutoResearchProxy so that I can pass it to a converter and manipulate several properties of the AutoResearchProxy and return a calculated price?
I would envision my converter looking like this.
public class PriceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is AutoResearchProxy)
{
var research = value as AutoResearchProxy;
//Some logic to figure out actual price
}
else
return String.Empty;
}
Hopefully this makes sense.
You can pass the whole datacontext-object to a Binding by not specifying a Path or by setting it to ., that however will result in the binding not updating if any of the relevant properties of that object change.
I would recommend you use a MultiBinding instead, that way you can target the necessary properties and the binding will update if any of those change. (For usage examples see the respective section on MSDN)
MyProperty="{Binding Converter={StaticResource ObjectToDerivedValueConverter}
That should do it.

Custom activity in WF 4.0: WorkflowItemsPresenter wont show converted array

we have an Array which is converted via a Binded Converter:
else if (TTools.IsOfBaseClass(value.GetType(), typeof(System.Activities.Presentation.Model.ModelItemCollection)))
{
OurBaseClass[] test = (value as ModelItemCollection).GetCurrentValue() as OurBaseClass[];
List<OurBaseClass> listOfArray = new List<OurBaseClass>();
foreach (OurBaseClass item in test)
{
listOfArray.Add(item);
}
return listOfArray;
}
the convertion works well but it is not shown in our dynamically gui
gui code with bindings:
<sap:WorkflowItemsPresenter xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation" Grid.Column="0" Name="MyArray" Items="{Binding Path=ModelItem.MyArray}" MinWidth="150" Margin="0">
<sap:WorkflowItemsPresenter.SpacerTemplate >
<DataTemplate>
<TextBlock Foreground="DarkGray" Margin="30">..</TextBlock>
</DataTemplate>
</sap:WorkflowItemsPresenter.SpacerTemplate>
<sap:WorkflowItemsPresenter.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" Margin="0"/>
</ItemsPanelTemplate>
</sap:WorkflowItemsPresenter.ItemsPanel>
</sap:WorkflowItemsPresenter>
Why is the gui not shown as a List??? it works well without converter.
Thanks
Have you tried setting a breakpoint in the converter?
I think the first problem may be that ModelItem.MyArray is type ModelProperty, rather than ModelItemCollection.

Categories