Db class propertys
[Serializable]
[EnableClientAccess()]
public class DbPersonelJobDetail
{
public DbPersonelJobDetail()
{
}
[Key]
public Guid PersonelID { get; set; }
public Guid JobID { get; set; }
public string JobName { get; set; }
public string Adi { get; set; }
}
DomainServices Linq Query
public IQueryable<DTO.DbPersonelJobDetail> GetPersonelJobTreeList()
{
IQueryable<DTO.DbPersonelJobDetail> result = from p in ObjectContext.SPA_PersonelJobDetail
join c in ObjectContext.SPA_PersonelJob on p.PersonelJobID equals c.ID
select new DTO.DbPersonelJobDetail()
{
JobID=p.PersonelJobID,
JobName = c.JobName,
PersonelID=p.ID,
Adi=p.Adi
};
return result.AsQueryable();
}
BindTreeList methot
public void BindTreeList()
{
loadOP = context.Load(context.GetPersonelJobTreeListQuery(), false);
loadOP.Completed += loadOP_Completed;
}
void loadOP_Completed(object sender, EventArgs e)
{
treeListPersonel.ItemsSource = loadOP.Entities;
}
I'm Treeview of binding BindTreeList() methot.
The following, as in the picture. HierarchicalDataTemplate Itemsource binding howto?
Could you make an example?
I could not :(
Waiting for your ideas...
Pucture
Load first lavel nodes.
In HierarchicalDataTemplate bind ItemsSource to LoadChildsConverter
<riaControls:DomainDataSource x:Name="MyData" QueryName="GetFirstLavel"
AutoLoad="True" LoadSize="50">
<riaControls:DomainDataSource.DomainContext>
<web:AdvDomainContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
<sdk:TreeView ItemsSource="{Binding}" DataContext="{Binding ElementName=MyData, Path=Data}">
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate
ItemsSource="{Binding Converter={StaticResource TreeViewCollectionConverter}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CODE}" />
<TextBlock Text="{Binding DESC}" />
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
TreeViewCollectionConverter.cs
public class TreeViewR5OBJECTCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ObservableCollection<Node> nodeList = new ObservableCollection<Node>();
if (value != null)
{
AdvDomainContext ctx = new AdvDomainContext();
Node parentNode = (Node)value;
ctx.Load(ctx.GetChildsQuery(parentNode), iop =>
{
foreach (var o in iop.Entities)
nodeList.Add(o);
}, null);
}
return nodeList;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
In AdvDomainService.cs
must have
public IQueryable<Node> GetFirstLavel()
to return first level nodes
and
public IQueryable<Node> GetChilds(Node ParentNode)
to return childs of ParentNode
Related
Oke here is the context: I have a Xamarin Application that connects to ASP.NET rest service. Currently I am working on the databinding on my views
There is a certain Data model called prestatie which has a foreingn Key Reference to the Trainer model class and another that foreign key reference to the Getuigschrift Class.
public class Prestatie : INotifyPropertyChanged
{
[Key]
private Guid _PrestatieID;
public Guid PrestatieID
{
get => _PrestatieID;
set
{
_PrestatieID = value;
RaisePropertyChanged(nameof(PrestatieID));
}
}
private string _Rekeningnummer;
public string Rekeningnummer
{
get => _Rekeningnummer;
set
{
_Rekeningnummer = value;
RaisePropertyChanged(nameof(Rekeningnummer));
}
}
private string _Rijksregisternummer;
public string Rijksregisternummer
{
get => _Rijksregisternummer;
set
{
_Rijksregisternummer = value;
RaisePropertyChanged(nameof(Rijksregisternummer));
}
}
[ForeignKeyAttribute("Trainer")]
public Guid? TrainerID
{
get;
set;
}
public Trainer Trainer
{
get;
set;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Trainer Class:
public class Trainer : Persoon
{
private Guid _TrainerID;
public Guid TrainerID
{
get => _TrainerID;
set
{
_TrainerID = value;
RaisePropertyChanged(nameof(TrainerID));
}
}
public ICollection<Prestatie> Prestaties
{
get;
set;
}
public Getuigschrift Getuigschriften
{
get;
set;
}
private Guid _GetuigschriftID;
public Guid? GetuigschriftID
{
get => _GetuigschriftID;
set
{
_GetuigschriftID = (Guid)value;
RaisePropertyChanged(nameof(GetuigschriftID));
}
}
}
Now I got two ViewModels setup fto represent this data, one for an overview and the second for the Details/Editing/adding. Databinding of items is on the viewmodels, so in Theory I should make these relations up in my view models but I am uncertain on how to do this. At one hand I just need to have some labels back in the ItemViewModel, and the other hand I need sort of comobox/list/picker for the data input to just get the Foregin Key.
Solutions I have tried it something like this, but that does not seem to work.
Xamarin ListView MVVM DataBinding
Here is a small snippet of my viewmodels, I cant post more because of the character limit.
public class PrestatieViewModel : BaseViewModel
{
private ObservableCollection<Prestatie> _prestaties;
private readonly IPrestatieDataService _prestatieDataService;
private readonly INavigationService _navigationService;
public ObservableCollection<Prestatie> Prestaties
{
get => _prestaties;
set
{
_prestaties = value;
OnPropertyChanged("Prestaties");
}
}
public class PrestatieDetailViewModel : BaseViewModel
{
private Prestatie _selectedPrestatie;
private readonly IPrestatieDataService _prestatieDataService;
private readonly INavigationService _navigationService;
public Prestatie SelectedPrestatie
{
get => _selectedPrestatie;
set
{
_selectedPrestatie = value;
OnPropertyChanged(nameof(SelectedPrestatie));
}
}
You can bind the picker's selectedItem with trainer so you could pick and set the value.
Here are the code you could refer to
xmal:
<ContentPage.BindingContext>
<local:PeopleViewModel/>
</ContentPage.BindingContext>
<CollectionView x:Name="mycol">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid ColumnDefinitions="100,* "
RowDefinitions="*,*">
<Label Text="{Binding Name}" BackgroundColor="LightBlue"/>
<Picker x:Name="mypicker" Grid.Column="1" Title="Select a Trainer"
TitleColor="Red"
ItemsSource="{Binding pgs}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding Trainer}">
</Picker>
<Label Text="Trainer:" Grid.Row="1" Grid.Column="0" BackgroundColor="AliceBlue"/>
<Label
Grid.Row="1"
Grid.Column="1"
Text="{Binding Trainer.Name}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
viewmodel:
public class PeopleViewModel:INotifyPropertyChanged
{
string name;
People trainer;
public List<People> pgs { get; private set; } = new List<People>{new People{Name="Trainer1" },
new People{ Name="Trainer2"} ,
new People{ Name="Trainer3"} };
public string Name {
get { return name; }
set { if (name != value)
{ name = value;
OnPropertyChanged();
}
} }
public People Trainer
{
get { return trainer; }
set
{
if (trainer != value)
{
trainer = value;
OnPropertyChanged();
}
}
}
#region INotifyPropertyChanged
void OnPropertyChanged(string name=null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
}}
I would like to know how to add image to the behaviour, actually i am trying to write the email behaviour where i am capable of changing the entry background but along with that i would like to add an image for it.
My xaml:
<StackLayout>
<Entry Placeholder="Enter a System.Double">
<Entry.Behaviors>
<local:CustomBehavior />
</Entry.Behaviors>
</Entry>
</StackLayout>
My Behaviour class:
public class CustomBehavior: Behavior<Entry>
{
private const string digitRegex = #"^(?("")("".+?(?<!\\)""#)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])#))" +
#"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs e)
{
Entry entry;
bool isValid;
entry =(Entry)sender;
isValid = Regex.IsMatch(e.NewTextValue, digitRegex);
entry.BackgroundColor = isValid ? Color.Default : Color.Red;
}
}
1st. For your request you have to add a separate Image to your layout, in your case:
<StackLayout
Orientation="Horizontal">
<Entry Placeholder="Enter a System.Double">
<Entry.Behaviors>
<local:CustomBehavior x:Name="customValidator"/>
</Entry.Behaviors>
</Entry>
<Image
HeightRequest="24"
WidthRequest="24"
Aspect="AspectFit"
IsVisible="{Binding Source={x:Reference customValidator},
Path=IsVisible}"
Style="{Binding Source={x:Reference customValidator},
Path=IsValid,
Converter={StaticResource boolToStyleImage}}"/>
</StackLayout>
Pay attention to the x:Name="" attribute as that is necessary to be able to reference that custom behavior within this xaml file
2nd. Create BindableProperties on your 'CustomBehavior' for two fields on which you'll bind the status of your image for your entry
public class CustomBehavior: Behavior<Entry>
{
private const string digitRegex = #"^(?("")("".+?(?<!\\)""#)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])#))" +
#"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
static readonly BindablePropertyKey IsValidPropertyKey = BindableProperty.CreateReadOnly ("IsValid", typeof (bool), typeof (CustomBehavior), false);
public static readonly BindableProperty IsValidProperty = IsValidPropertyKey.BindableProperty;
static readonly BindablePropertyKey IsVisiblePropertyKey = BindableProperty.CreateReadOnly ("IsVisible", typeof (bool), typeof (CustomBehavior), false);
public static readonly BindableProperty IsVisibleProperty = IsVisiblePropertyKey.BindableProperty;
private const string FRIEND = "friend";
private const string FRIENDS = "friends";
public bool IsValid
{
get { return (bool)base.GetValue (IsValidProperty); }
private set { base.SetValue (IsValidPropertyKey, value); }
}
public bool IsVisible
{
get { return (bool)base.GetValue (IsVisibleProperty); }
private set { base.SetValue (IsVisiblePropertyKey, value); }
}
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs e)
{
if (e.NewTextValue.Length > 0)
{
IsVisible = true;
Entry entry =(Entry)sender;
IsValid = Regex.IsMatch(e.NewTextValue, digitRegex);
if(IsValid) // Check only if we have a valid email
{
// Here we validate if the email contains our requirements
String email = entry.Text;
int pos = email.IndexOf("#"); // Exclude the domain
string username = email.Substring(0, pos);
if(username.Contains(FRIEND) || username.Contains(FRIENDS))
{
IsValid = true;
}else
IsValid = false;
}
}
}else
IsVisible = false;
}
}
3d. Create a simple ValueConverter class which will convert a boolean value to one of our objects of type 'T' depending on what we'll append this converter to.
namespace YourApp
public class BooleanToObjectConverter<T> : IValueConverter
{
public T FalseObject { set; get; }
public T TrueObject { set; get; }
public object Convert (object value, Type targetType,
object parameter, CultureInfo culture)
{
return (bool)value ? this.TrueObject : this.FalseObject;
}
public object ConvertBack (object value, Type targetType,
object parameter, CultureInfo culture)
{
return ((T)value).Equals (this.TrueObject);
}
}
4th. Add this style to ResourceDictionary tag on your App.xaml file which will declare the TrueObject(Style for a valid email) and the FalseObject(Style for an invalid email).
Replace "your_wrong_image_here.png" and "your_correct_image_here.png" to your desired images
<Application
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="YourApp.App"
xmlns:statics="clr-namespace:YourApp">
<Application.Resources>
<!-- Application resource dictionary -->
<ResourceDictionary>
<statics:BooleanToObjectConverter x:Key="boolToStyleImage"
x:TypeArguments="Style">
<statics:BooleanToObjectConverter.FalseObject>
<Style TargetType="Image">
<Setter Property="HeightRequest" Value="24" />
<Setter Property="Source"
Value="your_wrong_image_here.png" />
</Style>
</statics:BooleanToObjectConverter.FalseObject>
<statics:BooleanToObjectConverter.TrueObject>
<Style TargetType="Image">
<Setter Property="HeightRequest" Value="24" />
<Setter Property="Source"
Value="your_correct_image_here.png" />
</Style>
</statics:BooleanToObjectConverter.TrueObject>
</statics:BooleanToObjectConverter>
</ResourceDictionary>
</Application.Resources>
</Application>
This should fulfill your needs, just be careful from copy-paste errors as you have different class names on your project!
I think I got a threading problem in my UWP app.
I want to do a very simple thing:
a UI with 2 numeric fields;
if a numeric value is typed in field1, I want field2 to be set with a ratio of field1 (example: field2 = ratio * field1).
I am using x:Bind and TextChanging events. For unknown reasons, I wasn't able in the XAML to "call" the TextChanging event without having an exception at startup. Therefore, I am using the Loaded event.
Here's my model class, simply called MyModel:
public class MyModel : INotifyPropertyChanged
{
private readonly uint r1 = 3;
private uint _field1;
public uint Field1
{
get { return this._field1; }
set
{
this.Set(ref this._field1, value);
if (value == 0)
{
Field2 = 0;
}
else
{
Field2 = value * r1;
}
}
}
private uint _field2;
public uint Field2
{
get { return this._field2; }
set
{
this.Set(ref this._field2, value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisedPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
else
{
storage = value;
this.RaisedPropertyChanged(propertyName);
return true;
}
}
}
My ViewModel:
public class MyModelViewModel : INotifyPropertyChanged
{
public MyModel MyModel { get; set; }
public MyModelViewModel()
{
// Initialisation de notre page
this.MyModel = new MyModel()
{
Field1 = 0
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
my code behind (I'm filtering the input to avoid a cast exception):
public sealed partial class MainPage : Page
{
public MyModelViewModel ViewModel { get; set; } = new MyModelViewModel();
public MainPage()
{
this.InitializeComponent();
}
private void InitField1(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
field1.TextChanging += field1_TextChanging;
}
private void InitField2(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
field2.TextChanging += field2_TextChanging;
}
private void field1_TextChanging(TextBox sender, TextBoxTextChangingEventArgs args)
{
var error = errorTextBlock;
error.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
Regex regex = new Regex("[^0-9]+"); // All but numeric
if (regex.IsMatch(sender.Text))
{
error.Text = "Non numeric char";
error.Visibility = Windows.UI.Xaml.Visibility.Visible;
sender.Text = this.ViewModel.MyModel.Field1.ToString();
}
else
{
this.ViewModel.MyModel.Field1 = Convert.ToUInt32(sender.Text);
}
}
private void field2_TextChanging(TextBox sender, TextBoxTextChangingEventArgs args)
{
var error = errorTextBlock;
error.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
Regex regex = new Regex("[^0-9]+");
if (regex.IsMatch(sender.Text))
{
error.Text = "Non numeric char";
error.Visibility = Windows.UI.Xaml.Visibility.Visible;
sender.Text = this.ViewModel.MyModel.Field2.ToString();
}
else
{
this.ViewModel.MyModel.Field2 = Convert.ToUInt32(sender.Text);
}
}
}
Finally, my XAML:
<TextBlock Grid.Row="0" Grid.Column="0" x:Name="errorTextBlock" Text="" Visibility="Collapsed" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Field 1" />
<TextBox Grid.Row="1" Grid.Column="1" x:Name="field1" Text="{x:Bind ViewModel.MyModel.Field1, Mode=OneWay}" Loaded="InitField1" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="Field 2" />
<TextBox Grid.Row="2" Grid.Column="1" x:Name="field2" Text="{x:Bind ViewModel.MyModel.Field2, Mode=OneWay}" Loaded="InitField2" />
At runtime, if I type a non numeric char in field1, the input is filtered, field1 returns to its previous value without the screen "blinking" (that's why I use the TextChanging event and not the TextChanged). Perfect! But if I type a numeric char, field1 is correctly updated (I can see that with breakpoint), but when field2 is set, I got a native exception when RaisedPropertyChanged is called:
I'm suspecting some kind of threading error, but I'm pretty new to this kind of development. Any idea? Thanks!
Updated to use a separate 'Model' class
Here's how you can create a text box that when a number (integer) is entered into it another text box shows the entered number multiplied by another number.
Here's the UI. Note the Mode used for each binding and the second textbox is readonly because that's just for display.
<StackPanel>
<TextBlock Text="Value 1" />
<TextBox Text="{x:Bind ViewModel.MyModel.Value1, Mode=TwoWay}" />
<TextBlock Text="Value 2" />
<TextBox Text="{x:Bind ViewModel.MyModel.Value2, Mode=OneWay}" IsReadOnly="True" />
</StackPanel>
On the page I declare my Model
public MyViewModel ViewModel { get; set; } = new MyViewModel();
My ViewModel is very simple
public class MyViewModel
{
public MyModel MyModel { get; set; } = new MyModel();
}
The Model class contains the logic
public class MyModel : INotifyPropertyChanged
{
private string _value1;
public string Value1
{
get { return _value1; }
set
{
if (_value1 != value)
{
_value1 = value;
// Cause the updated value to be displayed on the UI
OnPropertyChanged(nameof(Value1));
// Is the entered value a number (int)?
int numericValue;
if (int.TryParse(value, out numericValue))
{
// It's a number so set the other value
// multiplied by the ratio
Value2 = (numericValue * 3).ToString();
}
else
{
// A number wasn't entered so indicate this
Value2 = "NaN";
}
// Cause the updated value2 to be displayed
OnPropertyChanged(nameof(Value2));
}
}
}
// We can use the automatic property here as don't need any logic
// relating the getting or setting this property
public string Value2 { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
With the above, when a number is entered for Value1 then Value2 will show a number three times as much (because I've set the ratio of 3).
You may notice that if you try the above that the change doesn't happen immediately and Value2 is only updated when the focus leaves the Value1 text box. This is because, by default, the two-way binding is only updated when focus is lost. This can easily be changed though.
If instead of using the new x:Bind method of binding we use the traditional Binding method we can force the binding to be updated whenever we want. Say, when the text is changed.
Modify the TextBox declaration like this:
<TextBox Text="{Binding ViewModel.Value1, Mode=TwoWay}"
TextChanged="TextBox_OnTextChanged" />
Note that the binding syntax is different and we've added an event.
The handler of the event is
private void TextBox_OnTextChanged(object sender, TextChangedEventArgs e)
{
var be = (sender as TextBox).GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
}
This forces the binding to update but there's another change we must make as well.
With the x:Bind syntax it tries to bind to the page. With the older Binding syntax it binds to the DataContext of the page. To make these the same, update the page constructor like this
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
Now the app will work again and Value2 will be updated after every key press in the Value1 text box.
I have an enum
public enum LookupTypes
{
[StringValue("UNIV")]
University,
[StringValue("COUR")]
Course}
How can i bind this in dropdownlist with UNIV as value and University as Text
c#/WPF
You'll need a few converters:
To display enum list ("University", "Course"), you'll need EnumValuesConverter, it's wrapping "enumType.getValues()"
Then you can use the provided extension method StringValue() to get the attribute's value.
Or use a converter EnumToStringValueConverter which does the same, to display the value on the UI (see Labels)
If by "with UNIV as value" you meant, combo.SelectedValue to be string UNIV tough luck, because SelectedValuePath doesn't handle functions, only properties.
Try this beast:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:app="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<app:EnumValuesConverter x:Key="enumValuesConverter" />
<app:EnumToStringValueConverter x:Key="enumToStringValueConverter" />
</Window.Resources>
<StackPanel x:Name="context">
<ComboBox x:Name="combo"
ItemsSource="{Binding Source={x:Type app:LookupTypes}, Mode=OneTime, Converter={StaticResource enumValuesConverter}}"
SelectedItem="{Binding EnumProp, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding}" ToolTip="{Binding Converter={StaticResource enumToStringValueConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label Content="{Binding EnumProp}" ContentStringFormat="Selected Enum: {0}" />
<Label Content="{Binding EnumProp, Converter={StaticResource enumToStringValueConverter}}" ContentStringFormat="Selected StringValue: {0}" />
<Button Click="Button_Click" Content="Alert enum" />
</StackPanel>
</Window>
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1 {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
context.DataContext = new MyViewModel {
EnumProp = LookupTypes.Course
};
}
private void Button_Click(object sender, RoutedEventArgs e) {
LookupTypes type = (LookupTypes)combo.SelectedItem;
MessageBox.Show(string.Format("Are you sure you want to use {0} ({1}) as lookup type?", type, type.StringValue()));
}
}
[global::System.AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public sealed class StringValueAttribute : Attribute {
public StringValueAttribute(string stringValue) {
StringValue = stringValue;
}
public string StringValue { get; private set; }
}
public static class StringValueExtensions {
public static string StringValue(this Enum This) {
System.Reflection.FieldInfo fieldInfo = This.GetType().GetField(This.ToString());
StringValueAttribute[] attribs = fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false) as StringValueAttribute[];
return attribs.Length == 0 ? null : attribs[0].StringValue;
}
}
public enum LookupTypes {
[StringValue("UNIV")]
University,
[StringValue("COUR")]
Course
}
class MyViewModel {
public LookupTypes EnumProp { get; set; }
}
[ValueConversion(typeof(Enum), typeof(string[]))]
public class EnumValuesConverter : IValueConverter {
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value == null) return Binding.DoNothing;
return Enum.GetValues((Type)value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
#endregion
}
[ValueConversion(typeof(Enum), typeof(string))]
public class EnumToStringValueConverter : DependencyObject, IValueConverter {
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
if (value == null) return Binding.DoNothing;
return ((Enum)value).StringValue();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
#endregion
}
}
i want to bind a list of images to a stackpanel which is inside a DataGrid.RowDetailsTemplate.
My class structure is as follows:
public class A
{
private List<MyImage> _images = new List<MyImage>();
public List<MyImage> Images { get; set; }
public string Name { get; set; }
public void AddImage(byte[] src) { ... }
}
public class MyImage
{
public BitmapImage Image { get; set; }
public byte[] RawData { get; set; }
}
In my main class i have a list of A:
public List<A> AList { get; set; }
dataGrid1.ItemsSource = AList;
dataGrid1.DataContext = AList;
All i want to do is to display the Name property of an element in a DataGridTextColumn and all images stored in the Images property in the RowDetails.
My xaml is:
<DataGrid name="dataGrid1">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Path=Name}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel DataContext="{Binding Path=Images}">
<Image Source="{Binding Path=RawData}"/>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
All i get to see is just one image although there are some more stored in Images. Any ideas?
Ok, so the solution of this problem was the use of ContentPresenter combined with a converter.
Now my XAML looks like this:
<DataGrid name="dataGrid1">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Path=Name}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Images, Converter={StaticResource ImageCollectionConverter}}"/>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
And the corresponding converter class:
public class ImageCollectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
List<MyImage> images = value as List<MyImage>;
if (images != null)
{
StackPanel stack = new StackPanel();
stack.Orientation = Orientation.Horizontal;
foreach (DesignImage img in images)
{
Image image = new Image();
image.Source = img.Image;
stack.Children.Add(image);
}
return stack;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}