LongListSelector doesn't show items - c#

I implement LongListSelector because I want to group items. But this LongListSelector dont show any item when I run it.
This is my XML source:
<?xml version="1.0" encoding="utf-8"?>
<Tasks>
<Task>
<Name>first task</Name>
<DueDate>05/03/2013 00:00:00</DueDate>
<Created>03/27/2013 01:24:08</Created>
</Task>
<Task>
<Name>second task</Name>
<DueDate>05/17/2013 00:00:00</DueDate>
<Created>03/27/2013 01:24:19</Created>
</Task>
<Task>
<Name>third task</Name>
<DueDate>05/17/2013 00:00:00</DueDate>
<Created>03/27/2013 01:24:38</Created>
</Task>
</Tasks>
My XAML code:
<toolkit:LongListSelector Background="Transparent" ItemsSource="{Binding TasksByDueDate}">
<toolkit:LongListSelector.GroupHeaderTemplate>
<DataTemplate>
<Border Background="Transparent">
<Border Background="{StaticResource PhoneAccentBrush}" Width="475" Height="35" HorizontalAlignment="Left">
<TextBlock Text="{Binding Key}"
Foreground="{StaticResource PhoneForegroundBrush}"
Style="{StaticResource PhoneTextGroupHeaderStyle}"
VerticalAlignment="Bottom"/>
</Border>
</Border>
</DataTemplate>
</toolkit:LongListSelector.GroupHeaderTemplate>
<toolkit:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Orientation="Horizontal">
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" Width="345"/>
</StackPanel>
</DataTemplate>
</toolkit:LongListSelector.ItemTemplate>
</toolkit:LongListSelector>
MainViewModel.cs:
public class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Task> Tasks { get; private set; }
public IEnumerable<Group<string, Task>> TasksByDueDate { get; private set; }
public MainViewModel()
{
this.Tasks = new ObservableCollection<Task>();
this.Projects = new ObservableCollection<Project>();
this.Contexts = new ObservableCollection<Context>();
}
public bool IsDataLoaded { get; private set; }
public void LoadPlannedData()
{
try
{
Tasks.Clear();
var file = IsolatedStorageFile.GetUserStoreForApplication();
XElement xElem;
using (IsolatedStorageFileStream read = file.OpenFile("tasks.xml", FileMode.Open))
{
xElem = XElement.Load(read);
}
var tasks = from task in xElem.Elements("Task")
orderby (DateTime)task.Element("Created") descending
select task;
foreach (XElement xElemItem in tasks)
{
Tasks.Add(new Task
{
Name = xElemItem.Element("Name").Value.ToString(),
DueDate = xElemItem.Element("DueDate").Value.ToString(),
Created = xElemItem.Element("Created").Value.ToString()
});
}
TasksByDueDate = from c in Tasks
group c by c.DueDate into n
select new Group<string, Task>(n);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
this.IsDataLoaded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Group.cs:
public class Group<TKey, TElement> : IGrouping<TKey, TElement>
{
private readonly IGrouping<TKey, TElement> grouping;
public Group(IGrouping<TKey, TElement> unit)
{
grouping = unit;
}
public TKey Key
{
get { return grouping.Key; }
}
public IEnumerator<TElement> GetEnumerator()
{
return grouping.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return grouping.GetEnumerator();
}
}
If I show ListBox of Task then everything is Displayed well. But if I display grouped items then, no item is displayed.
Aplication runs wel (no crash) only items in LongListSelector missing.
What am I doing wrong?

I spent 2-3 days chasing down this same issue and it was a nightmare. I believe the answer is that the ItemsSource data format needs to be IList. Unfortunately that won't really help you much, but here are two classes that I've created that ... well they work at least.
My StringKeyGroup allows full text group names, whilst the AlphaKeyGroup alphabetises the list.
StringKeyGroup class
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
namespace LongListSelectorDemo.Model
{
public class StringKeyGroup<T> : ObservableCollection<T>
{
public delegate string GetKeyDelegate(T item);
public string Key { get; private set; }
public StringKeyGroup(string key)
{
Key = key;
}
public static ObservableCollection<StringKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, GetKeyDelegate getKey, bool sort)
{
var list = new ObservableCollection<StringKeyGroup<T>>();
foreach (var item in items)
{
var index = -1;
for (var i = 0; i < list.Count; i++)
{
if (list[i].Key.Equals(getKey(item)))
{
index = i;
break;
}
}
if (index == -1)
{
list.Add(new StringKeyGroup<T>(getKey(item)));
index = list.Count - 1;
}
if (index >= 0 && index < list.Count)
{
list[index].Add(item);
}
}
if (sort)
{
foreach (var group in list)
{
group.ToList().Sort((c0, c1) => ci.CompareInfo.Compare(getKey(c0), getKey(c1)));
}
}
return list;
}
}
}
AlphaKeyGroup class
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using Microsoft.Phone.Globalization;
namespace LongListSelectorDemo.Model
{
public class AlphaKeyGroup<T> : ObservableCollection<T>
{
/// <summary>
/// The delegate that is used to get the key information.
/// </summary>
/// <param name="item">An object of type T</param>
/// <returns>The key value to use for this object</returns>
public delegate string GetKeyDelegate(T item);
/// <summary>
/// The Key of this group.
/// </summary>
public string Key { get; private set; }
/// <summary>
/// Public constructor.
/// </summary>
/// <param name="key">The key for this group.</param>
public AlphaKeyGroup(string key)
{
Key = key;
}
/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="slg">The </param>
/// <returns>Theitems source for a LongListSelector</returns>
private static ObservableCollection<AlphaKeyGroup<T>> CreateGroups(SortedLocaleGrouping slg)
{
return new ObservableCollection<AlphaKeyGroup<T>>(slg.GroupDisplayNames.Select(key => new AlphaKeyGroup<T>(key)).ToList());
}
/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="items">The items to place in the groups.</param>
/// <param name="ci">The CultureInfo to group and sort by.</param>
/// <param name="getKey">A delegate to get the key from an item.</param>
/// <param name="sort">Will sort the data if true.</param>
/// <returns>An items source for a LongListSelector</returns>
public static ObservableCollection<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, GetKeyDelegate getKey, bool sort)
{
var slg = new SortedLocaleGrouping(ci);
var list = CreateGroups(slg);
foreach (var item in items)
{
var index = 0;
if (slg.SupportsPhonetics)
{
//check if your database has yomi string for item
//if it does not, then do you want to generate Yomi or ask the user for this item.
//index = slg.GetGroupIndex(getKey(Yomiof(item)));
}
else
{
index = slg.GetGroupIndex(getKey(item));
}
if (index >= 0 && index < list.Count)
{
list[index].Add(item);
}
}
if (sort)
{
foreach (var group in list)
{
group.ToList().Sort((c0, c1) => ci.CompareInfo.Compare(getKey(c0), getKey(c1)));
}
}
return list;
}
}
}
Example usage, just swap the class name between StringKeyGroup and AlphaKeyGroup to see the difference:
XAML
<phone:PhoneApplicationPage
x:Class="LongListSelectorDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector x:Name="GroupedList" IsGroupingEnabled="True" HideEmptyGroups="True">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Padding="{StaticResource PhoneTouchTargetOverhang}"
FontSize="{StaticResource PhoneFontSizeMediumLarge}" />
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
<phone:LongListSelector.GroupHeaderTemplate>
<DataTemplate>
<Border Background="{StaticResource PhoneAccentBrush}"
Padding="{StaticResource PhoneTouchTargetOverhang}">
<TextBlock Text="{Binding Key}" Style="{StaticResource PhoneTextGroupHeaderStyle}"/>
</Border>
</DataTemplate>
</phone:LongListSelector.GroupHeaderTemplate>
</phone:LongListSelector>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
XAML.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using LongListSelectorDemo.Model;
using Microsoft.Phone.Controls;
namespace LongListSelectorDemo
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (GroupedList.ItemsSource == null)
{
var foodItems = new ObservableCollection<FoodItem>();
/*---Make burger items---*/
foodItems.Add(new FoodItem("Hamburger", "Burgers"));
foodItems.Add(new FoodItem("Chicken burger", "Burgers"));
foodItems.Add(new FoodItem("Turkey burger", "Burgers"));
foodItems.Add(new FoodItem("Black bean burger", "Burgers"));
/*---Make fryer items---*/
foodItems.Add(new FoodItem("Fries", "Fryer"));
foodItems.Add(new FoodItem("Onion rings", "Fryer"));
foodItems.Add(new FoodItem("Tater tots", "Fryer"));
foodItems.Add(new FoodItem("Mozzarella sticks", "Fryer"));
/*---Make fish items---*/
foodItems.Add(new FoodItem("Salmon", "Fish"));
foodItems.Add(new FoodItem("Rainbow trout", "Fish"));
foodItems.Add(new FoodItem("Grilled tilapia", "Fish"));
GroupedList.ItemsSource = GroupedItems(foodItems);
}
}
public ObservableCollection<StringKeyGroup<FoodItem>> GroupedItems(IEnumerable<FoodItem> source)
{
return StringKeyGroup<FoodItem>.CreateGroups(source,
System.Threading.Thread.CurrentThread.CurrentUICulture, s => s.GroupName, true);
}
}
}
Here is the FoodItem class:
namespace LongListSelectorDemo.Model
{
public class FoodItem
{
public FoodItem(string name, string groupName)
{
Name = name;
GroupName = groupName;
}
public string Name { get; private set; }
public string GroupName { get; private set; }
}
}

Related

IDataErrorInfo only works when property has been set and cleared again

So, in my code I use IDataError to do validation, but I am seeing some strange behavior lately on some views I have made.
My class implements INotifyPropertyChanged as well as IDataErrorInfo and this is my model class:
using System;
using System.ComponentModel;
using TMWpfClient.Models.System.Base;
namespace TMWpfClient.Models.Tournament.Operation
{
public class MatchComplaintInfo : BaseEntity, IDataErrorInfo
{
#region private variables
private string _complaint;
private string _filedBy;
private Guid _matchId;
#endregion
#region constructors
public MatchComplaintInfo() : base() { }
public MatchComplaintInfo(Guid matchid) : base()
{
MatchId = matchid;
}
public MatchComplaintInfo(Guid id, Guid matchid, string complaint, string filedby, DateTime created,
Guid createdby) : base(id, created, createdby)
{
_complaint = complaint;
_filedBy = filedby;
_matchId = matchid;
}
#endregion
#region public properties
public Guid MatchId { get; set; }
public string Complaint
{
get => _complaint;
set
{
if (_complaint == value)
return;
_complaint = value;
IsDirty = true;
OnPropertyChanged("Complaint");
}
}
public string FiledBy
{
get => _filedBy;
set
{
if (_filedBy == value)
return;
_filedBy = value;
IsDirty = true;
OnPropertyChanged("FiledBy");
}
}
#endregion
public string this[string columnName]
{
get
{
switch (columnName)
{
case "Complaint" when string.IsNullOrEmpty(Complaint):
return "The complaint has to filled out!";
case "FiledBy" when string.IsNullOrEmpty(FiledBy):
return "A name of the person filing the complaint has to be supplied";
default:
return string.Empty;
}
}
}
public string Error { get; }
}
}
My BaseEntity class:
public class BaseEntity : ObservableObject, IDataStatus, IDirty, IBaseEntity
{
private Guid _id;
private DateTime _created;
private Guid _createdBy;
private bool _isDirty;
private bool _isNew;
public Guid Id
{
get { return _id; }
set { _id = value; }
}
public DateTime Created
{
get { return _created; }
set { _created = value; }
}
public Guid CreatedBy
{
get { return _createdBy; }
set { _createdBy = value; }
}
public BaseEntity()
{
Id = Guid.NewGuid();
Created = DateTime.Now;
IsNew = IsDirty = true;
}
public BaseEntity(Guid id, DateTime created, Guid createdby)
{
_id = id;
_created = created;
_createdBy = createdby;
}
#region datastatus
public bool IsDirty
{
get { return _isDirty; }
set
{
if (_isDirty == value)
return;
_isDirty = value;
OnPropertyChanged("IsDirty");
}
}
public bool IsNew
{
get => _isNew;
set
{
if (_isNew == value)
return;
_isNew = value;
OnPropertyChanged("IsNew");
}
}
public bool IsDeleted { get; set; }
public List<string> ChangedProperties { get; set; } = new List<string>();
#endregion
}
}
My ObservableObject class:
public abstract class ObservableObject : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
if (this.PropertyChanged != null)
{
var e = new PropertyChangedEventArgs(propertyName);
this.PropertyChanged(this, e);
}
}
#endregion // INotifyPropertyChanged Members
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
}
My view model used in my user control:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using Telerik.Windows.Controls.GridView;
using TMServerAPIContracts.Models.Enums;
using TMServerAPIContracts.Models.MatchTypes;
using TMWpfClient.Common;
using TMWpfClient.Extensions.DataConverters;
using TMWpfClient.Models.Application;
using TMWpfClient.Models.MatchType;
using TMWpfClient.Models.System.Base;
using TMWpfClient.Models.Tournament;
using TMWpfClient.Models.Tournament.Operation;
using TMWpfClient.Models.Tournament.Planning.Schedule;
using TMWpfClient.Properties;
using TMWpfClient.ViewModels.System;
using TMWpfClient.Views.Core;
using MatchTypePenaltySequence = TMWpfClient.Models.MatchType.MatchTypePenaltySequence;
namespace TMWpfClient.ViewModels.Tournament.Operations
{
public class MatchExecutionBaseViewModel : ObservableObject, IDataErrorInfo
{
#region private variables
private bool _addComplaint;
private bool _activateComplaintSetting = true;
private bool _noshowAvailable = true;
private MatchComplaintInfo _complaint;
#endregion
#region public properties
#region handling complaint
/// <summary>
/// Indicating whether a complaint is/should be added
/// </summary>
public bool AddComplaint
{
get => _addComplaint;
set
{
if (_addComplaint == value)
return;
_addComplaint = value;
OnPropertyChanged("AddComplaint");
}
}
/// <summary>
/// Is the setting for complaint editable
/// </summary>
public bool ActivateComplaintSetting
{
get => _activateComplaintSetting;
set
{
if (_activateComplaintSetting == value)
return;
_activateComplaintSetting = value;
OnPropertyChanged("ActivateComplaintSetting");
}
}
public MatchComplaintInfo Complaint
{
get => _complaint;
set
{
_complaint = value;
OnPropertyChanged("Complaint");
}
}
#endregion
#endregion
#region constructor
public MatchExecutionBaseViewModel()
{
//creating an empty complaint ready to fill
Complaint = new MatchComplaintInfo(match.Id);
}
#endregion
}
}
XAML Grid for my user control:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<!-- labels -->
<TextBlock Grid.Column="0" Grid.Row="0" Margin="2">Create complaint</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1" Margin="2">Complaint</TextBlock>
<TextBlock Grid.Column="0" Grid.Row="2" Margin="2">Filed by</TextBlock>
<CheckBox Grid.Column="1" Grid.Row="0" Margin="2" ToolTip="Check this to create complaint" IsChecked="{Binding AddComplaint, Mode=TwoWay}" IsEnabled="{Binding ActivateComplaintSetting}"/>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding Complaint.Complaint, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Margin="2" VerticalContentAlignment="Top" IsEnabled="{Binding AddComplaint}" Validation.ErrorTemplate="{StaticResource ValidationErrorTemplateLeft}"/>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Complaint.FiledBy, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" Margin="2" Validation.ErrorTemplate="{StaticResource ValidationErrorTemplateLeft}" IsEnabled="{Binding AddComplaint}"/>
</Grid>
and my control template for displaying the validation error:
<ControlTemplate x:Key="ValidationErrorTemplateLeft">
<Border BorderBrush="Red" BorderThickness="1">
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
<Grid Width="12" Height="12">
<Ellipse Width="12" Height="12"
Fill="Red" HorizontalAlignment="Center"
VerticalAlignment="Center"
></Ellipse>
<TextBlock Foreground="White" FontWeight="Heavy"
FontSize="8" HorizontalAlignment="Center"
VerticalAlignment="Center" TextAlignment="Center"
ToolTip="{Binding ElementName=ErrorAdornerLeft, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">X</TextBlock>
</Grid>
</StackPanel>
<AdornedElementPlaceholder x:Name="ErrorAdornerLeft"></AdornedElementPlaceholder>
</DockPanel>
</Border>
</ControlTemplate>
When debugging, I see that the validation code is triggered, and the error text is returned, but the validation template is not displayed.
But if I enter a value in the textbox, and then clears it again, then it displays the error template. Any ideas why I see this behaviour?
I would expect the two textfields to light up in red initially,and be gone when enetering data into the fields.

WPF ListView data binding with Image dopesn't work

I have this code, which is supposed to display images of the product in a listview:
namespace BoeshCRM
{
/// <summary>
/// Interaction logic for NewBill.xaml
/// </summary>
public partial class NewBill : Page
{
ObservableCollection<ProdImg> Inventory
{
get { return (ObservableCollection<ProdImg>)GetValue(InventoryProperty); }
set { SetValue(InventoryProperty, value); }
}
public static readonly DependencyProperty InventoryProperty =
DependencyProperty.Register("Inventory", typeof(ObservableCollection<ProdImg>), typeof(MainWindow), null);
public NewBill()
{
InitializeComponent();
Inventory = new ObservableCollection<ProdImg>();
Inventory.Add(new ProdImg(Product.LoadProduct(13L)));
}
}
}
This is the actual XML for the Page
<Page x:Class="BoeshCRM.NewBill"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="NewBill">
<Grid>
<ListView x:Name="lstItems" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" MinWidth="50" MinHeight="50" ItemsSource="{Binding Inventory}">
<ListView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding imgFi}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Page>
And this is the code for the helper classes, and the ProdImg struct.
namespace BoeshCRM
{
struct ProdImg
{
public Product p;
public FileInfo imgFi;
public ProdImg(Product _p)
{
p = _p;
imgFi = new FileInfo("C:/" + p.id.ToString() + ".jpg");
}
}
}
However no Image is displayed. Any idea why?
The LoadProduct method is not the problem, it works just fine.
Create property in ProdImg and bind to its FullName
public class ProdImg
{
public Product p;
public FileInfo imgFi;
public ProdImg(Product _p)
{
p = _p;
imgFi = new FileInfo("C:/" + p.id.ToString() + ".jpg");
}
public FileInfo ImgFi
{
get { return imgFi ; }
set {imgFi = value; }
}
}
<Image Source="{Binding ImgFi.FullName}" />

Using a class to bind settings (Windows Phone)

I'm new to WP development and I have a problem I can't get fixed on my own.
I'm trying to set up a settings page. I copied most of it from that msdn article:
http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff769510(v=vs.105).aspx#BKMK_CreatingaSettingsPageThatDoesNotRequireaConfirmationButton
The problem is connected to this line:
<phone:PhoneApplicationPage.Resources>
<local:Page1 x:Key="appSettings" />
</phone:PhoneApplicationPage.Resources>
It behaves completly random. Most of the time VS just crashes.
I am sure that this is because VS tries to connect to the IsolatedStorage. But the Emulator just closes the App when I try to access the page.
The full code of the settings page (Page1):
Page1.xaml:
<phone:PhoneApplicationPage
x:Class="MyApp.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyApp"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
shell:SystemTray.IsVisible="True">
<phone:PhoneApplicationPage.Resources>
<local:Page1 x:Key="appSettings" />
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="MYAPP" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="settings" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<Grid x:Name="ContentGrid" Grid.Row="1">
<CheckBox Content="CheckBox Setting" Height="Auto" HorizontalAlignment="Left" Margin="60,20,0,0" Name="checkBoxSetting" VerticalAlignment="Top"
IsChecked="{Binding Source={StaticResource appSettings}, Path=CheckBoxSetting, Mode=TwoWay}" />
<ListBox Height="140" HorizontalAlignment="Left" Margin="70,150,0,0" Name="listBoxSetting"
VerticalAlignment="Top" Width="360" SelectedIndex="{Binding Source={StaticResource appSettings}, Path=ListBoxSetting, Mode=TwoWay}">
<ListBoxItem Content="Times New Roman" FontSize="24" FontFamily="Times New Roman" />
<ListBoxItem Content="Arial" FontSize="24" FontFamily="Arial" />
<ListBoxItem Content="Comic Sans MS" FontSize="24" FontFamily="Comic Sans MS" />
</ListBox>
<RadioButton Content="Choice One" Height="Auto" HorizontalAlignment="Left" Margin="60,0,0,235" Name="radioButton1" VerticalAlignment="Bottom" GroupName="GroupOne" IsChecked="{Binding Source={StaticResource appSettings}, Path=RadioButton1Setting, Mode=TwoWay}" />
<RadioButton Content="Choice Two" Height="Auto" HorizontalAlignment="Left" Margin="60,350,0,0" Name="radioButton2" VerticalAlignment="Top" GroupName="GroupOne" IsChecked="{Binding Source={StaticResource appSettings}, Path=RadioButton2Setting, Mode=TwoWay}"/>
<RadioButton Content="Choice Three" Height="Auto" HorizontalAlignment="Left" Margin="60,400,0,0" Name="radioButton3" VerticalAlignment="Top" GroupName="GroupOne" IsChecked="{Binding Source={StaticResource appSettings}, Path=RadioButton3Setting, Mode=TwoWay}"/>
</Grid>
</Grid>
</phone:PhoneApplicationPage>
And the Page1.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.IO.IsolatedStorage;
using System.Diagnostics;
namespace Schedule
{
public partial class Page1 : PhoneApplicationPage
{
// Our settings
IsolatedStorageSettings settings;
// The key names of our settings
const string CheckBoxSettingKeyName = "CheckBoxSetting";
const string ListBoxSettingKeyName = "ListBoxSetting";
const string RadioButton1SettingKeyName = "RadioButton1Setting";
const string RadioButton2SettingKeyName = "RadioButton2Setting";
const string RadioButton3SettingKeyName = "RadioButton3Setting";
const string UsernameSettingKeyName = "UsernameSetting";
const string PasswordSettingKeyName = "PasswordSetting";
// The default value of our settings
const bool CheckBoxSettingDefault = true;
const int ListBoxSettingDefault = 0;
const bool RadioButton1SettingDefault = true;
const bool RadioButton2SettingDefault = false;
const bool RadioButton3SettingDefault = false;
const string UsernameSettingDefault = "";
const string PasswordSettingDefault = "";
/// <summary>
/// Constructor that gets the application settings.
/// </summary>
public Page1()
{
InitializeComponent();
// Get the settings for this application.
try
{
settings = IsolatedStorageSettings.ApplicationSettings;
}
catch(System.IO.IsolatedStorage.IsolatedStorageException e)
{
MessageBox.Show(e.ToString());
}
}
/// <summary>
/// Update a setting value for our application. If the setting does not
/// exist, then add the setting.
/// </summary>
/// <param name="Key"></param>
/// <param name="value"></param>
/// <returns></returns>
public bool AddOrUpdateValue(string Key, Object value)
{
bool valueChanged = false;
// If the key exists
if (settings.Contains(Key))
{
// If the value has changed
if (settings[Key] != value)
{
// Store the new value
settings[Key] = value;
valueChanged = true;
}
}
// Otherwise create the key.
else
{
settings.Add(Key, value);
valueChanged = true;
}
return valueChanged;
}
/// <summary>
/// Get the current value of the setting, or if it is not found, set the
/// setting to the default setting.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Key"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public T GetValueOrDefault<T>(string Key, T defaultValue)
{
T value;
// If the key exists, retrieve the value.
if (settings.Contains(Key))
{
value = (T)settings[Key];
}
// Otherwise, use the default value.
else
{
value = defaultValue;
}
return value;
}
/// <summary>
/// Save the settings.
/// </summary>
public void Save()
{
settings.Save();
}
/// <summary>
/// Property to get and set a CheckBox Setting Key.
/// </summary>
public bool CheckBoxSetting
{
get
{
return GetValueOrDefault<bool>(CheckBoxSettingKeyName, CheckBoxSettingDefault);
}
set
{
if (AddOrUpdateValue(CheckBoxSettingKeyName, value))
{
Save();
}
}
}
/// <summary>
/// Property to get and set a ListBox Setting Key.
/// </summary>
public int ListBoxSetting
{
get
{
return GetValueOrDefault<int>(ListBoxSettingKeyName, ListBoxSettingDefault);
}
set
{
if (AddOrUpdateValue(ListBoxSettingKeyName, value))
{
Save();
}
}
}
/// <summary>
/// Property to get and set a RadioButton Setting Key.
/// </summary>
public bool RadioButton1Setting
{
get
{
return GetValueOrDefault<bool>(RadioButton1SettingKeyName, RadioButton1SettingDefault);
}
set
{
if (AddOrUpdateValue(RadioButton1SettingKeyName, value))
{
Save();
}
}
}
/// <summary>
/// Property to get and set a RadioButton Setting Key.
/// </summary>
public bool RadioButton2Setting
{
get
{
return GetValueOrDefault<bool>(RadioButton2SettingKeyName, RadioButton2SettingDefault);
}
set
{
if (AddOrUpdateValue(RadioButton2SettingKeyName, value))
{
Save();
}
}
}
/// <summary>
/// Property to get and set a RadioButton Setting Key.
/// </summary>
public bool RadioButton3Setting
{
get
{
return GetValueOrDefault<bool>(RadioButton3SettingKeyName, RadioButton3SettingDefault);
}
set
{
if (AddOrUpdateValue(RadioButton3SettingKeyName, value))
{
Save();
}
}
}
/// <summary>
/// Property to get and set a Username Setting Key.
/// </summary>
public string UsernameSetting
{
get
{
return GetValueOrDefault<string>(UsernameSettingKeyName, UsernameSettingDefault);
}
set
{
if (AddOrUpdateValue(UsernameSettingKeyName, value))
{
Save();
}
}
}
/// <summary>
/// Property to get and set a Password Setting Key.
/// </summary>
public string PasswordSetting
{
get
{
return GetValueOrDefault<string>(PasswordSettingKeyName, PasswordSettingDefault);
}
set
{
if (AddOrUpdateValue(PasswordSettingKeyName, value))
{
Save();
}
}
}
}
}
Am I would be very happy about any help.
Thanks.
Instead of local:Page1, you're supposed to put a separate class (named AppSettings in the MSDN sample).
In your case, you have put an instance of Page1 in the XAML of... Page1! Therefore, when the runtime creates Page1, it parses the XAML, find this line of code, create the new Page1 instance, which will in turn create a Page1 instance, and so on...

ListView's item insert animations in WinRT

Recently I started building my own big Windows 8 Store App.
Working on UI I started replicating some good UIs.
One I met very interesting animation of inserting new elements in list view in standard Mail app. When you click on chain it expands and shows all messages in chain.
Here is captured video.
I have no idea what technique did they use to achieve this animation and behavior.
Can anyone help me, explain or give example how can I achieve such behavior? Thanks.
The mail app is written in JavaScript, so it won't help you much to know how it was done since this UI stack is quite different than the XAML one. The thing though is that the list controls are likely animated the same way, so you only need to add/remove some items in the list to get the expansion/collapse effect.
I played with it for a bit and this is what I came up with that uses ListView's ItemTemplateSelector property to define a few different item templates.
<Page
x:Class="App82.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App82"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<local:CollapsibleListItemTemplateSelector
x:Key="collapsibleListItemTemplateSelector">
<local:CollapsibleListItemTemplateSelector.BasicItemTemplate>
<DataTemplate>
<Border
Margin="5"
Height="50"
VerticalAlignment="Stretch"
BorderBrush="ForestGreen"
BorderThickness="2,0,0,0">
<StackPanel
Margin="10,0,0,0">
<TextBlock
FontWeight="Bold"
Text="{Binding Title}" />
<TextBlock
Text="{Binding Gist}" />
</StackPanel>
</Border>
</DataTemplate>
</local:CollapsibleListItemTemplateSelector.BasicItemTemplate>
<local:CollapsibleListItemTemplateSelector.ExpandedItemTemplate>
<DataTemplate>
<Border
Margin="15,5,5,5"
Height="50"
VerticalAlignment="Stretch"
BorderBrush="Yellow"
BorderThickness="2,0,0,0">
<StackPanel
Margin="10,0,0,0">
<TextBlock
FontWeight="Bold"
Text="{Binding Title}" />
<TextBlock
Text="{Binding Gist}" />
</StackPanel>
</Border>
</DataTemplate>
</local:CollapsibleListItemTemplateSelector.ExpandedItemTemplate>
<local:CollapsibleListItemTemplateSelector.CollapsibleItemTemplate>
<DataTemplate>
<Border
Margin="5"
Height="50"
VerticalAlignment="Stretch"
BorderBrush="DodgerBlue"
BorderThickness="2,0,0,0">
<StackPanel
Margin="10,0,0,0"
Orientation="Horizontal">
<TextBlock
FontWeight="Bold"
Text="{Binding ChildItems.Count}" />
<TextBlock
FontWeight="Bold"
Text=" Items" />
</StackPanel>
</Border>
</DataTemplate>
</local:CollapsibleListItemTemplateSelector.CollapsibleItemTemplate>
</local:CollapsibleListItemTemplateSelector>
</Page.Resources>
<Grid
Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ListView
x:Name="ListView"
ItemTemplateSelector="{StaticResource collapsibleListItemTemplateSelector}"
ItemClick="OnItemClick"
IsItemClickEnabled="True" />
</Grid>
</Page>
Code behind:
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using App82.Common;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace App82
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
var items = new ObservableCollection<BindableBase>();
var item1 = new BasicItem { Title = "Item 1", Gist = "This item has some content that is not fully shown..." };
var item2 = new ExpandedItem { Title = "Item 2", Gist = "This item has some content that is not fully shown..." };
var item3 = new ExpandedItem { Title = "Item 3", Gist = "This item has some content that is not fully shown..." };
var item4 = new ExpandedItem { Title = "Item 4", Gist = "This item has some content that is not fully shown..." };
var item5 = new BasicItem { Title = "Item 5", Gist = "This item has some content that is not fully shown..." };
var itemGroup1 = new CollapsibleItem(items, new[] { item2, item3, item4 });
items.Add(item1);
items.Add(itemGroup1);
items.Add(item5);
this.ListView.ItemsSource = items;
}
private void OnItemClick(object sender, ItemClickEventArgs e)
{
var collapsibleItem = e.ClickedItem as CollapsibleItem;
if (collapsibleItem != null)
collapsibleItem.ToggleCollapse();
}
}
public class CollapsibleListItemTemplateSelector : DataTemplateSelector
{
public DataTemplate BasicItemTemplate { get; set; }
public DataTemplate CollapsibleItemTemplate { get; set; }
public DataTemplate ExpandedItemTemplate { get; set; }
protected override Windows.UI.Xaml.DataTemplate SelectTemplateCore(object item, Windows.UI.Xaml.DependencyObject container)
{
if (item is ExpandedItem)
return ExpandedItemTemplate;
if (item is BasicItem)
return BasicItemTemplate;
//if (item is CollapsibleItem)
return CollapsibleItemTemplate;
}
}
public class BasicItem : BindableBase
{
#region Title
private string _title;
public string Title
{
get { return _title; }
set { this.SetProperty(ref _title, value); }
}
#endregion
#region Gist
private string _gist;
public string Gist
{
get { return _gist; }
set { this.SetProperty(ref _gist, value); }
}
#endregion
}
public class ExpandedItem : BasicItem
{
}
public class CollapsibleItem : BindableBase
{
private readonly IList _hostCollection;
#region IsExpanded
private bool _isExpanded;
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (this.SetProperty(ref _isExpanded, value))
{
if (_isExpanded)
Expand();
else
Collapse();
}
}
}
#endregion
#region ChildItems
private ObservableCollection<BasicItem> _childItems;
public ObservableCollection<BasicItem> ChildItems
{
get { return _childItems; }
set { this.SetProperty(ref _childItems, value); }
}
#endregion
public CollapsibleItem(
IList hostCollection,
IEnumerable<BasicItem> childItems)
{
_hostCollection = hostCollection;
_childItems = new ObservableCollection<BasicItem>(childItems);
}
public void ToggleCollapse()
{
IsExpanded = !IsExpanded;
}
private void Expand()
{
int i = _hostCollection.IndexOf(this) + 1;
foreach (var childItem in ChildItems)
{
_hostCollection.Insert(i++, childItem);
}
}
private void Collapse()
{
int i = _hostCollection.IndexOf(this) + 1;
for (int index = 0; index < ChildItems.Count; index++)
{
_hostCollection.RemoveAt(i);
}
}
}
}

Class Deserialisation and WPF bindings

I have a little puzzle I'm trying to solve and am not sure how to go about it...
WPF application based on MVVM approach...
I have a SubstituionDataSet class that inherits from DataSet and defines an additional collection:
namespace Lib
{
public class SubstitutionDataSet : DataSet
{
public SubstitutionDataSet()
{
TableNames = new ObservableCollection<SubstitutionDataTable>();
Tables.CollectionChanging += DataTablesCollectionChanging;
}
public ObservableCollection<SubstitutionDataTable> TableNames { get; set; }
private void DataTablesCollectionChanging(object sender, CollectionChangeEventArgs e)
{
var actionTable = (DataTable) e.Element;
if (e.Action == CollectionChangeAction.Add)
{
actionTable.Columns.CollectionChanged += DataColumnCollectionChanged;
TableNames.Add(new SubstitutionDataTable { Name = actionTable.TableName });
}
else if (e.Action == CollectionChangeAction.Remove)
{
actionTable.Columns.CollectionChanged -= DataColumnCollectionChanged;
TableNames.Remove(TableNames.First(tn => tn.Name == actionTable.TableName));
}
}
private void DataColumnCollectionChanged(object sender, CollectionChangeEventArgs e)
{
var actionColumn = (DataColumn) e.Element;
var hostTable = (DataTable) actionColumn.Table;
var hostSubsitutionTable = TableNames.First(tn => tn.Name == hostTable.TableName);
if (e.Action == CollectionChangeAction.Add)
{
hostSubsitutionTable.ColumnNames.Add(actionColumn.ColumnName);
}
else if (e.Action == CollectionChangeAction.Remove)
{
hostSubsitutionTable.ColumnNames.Remove(hostSubsitutionTable.ColumnNames.First(cn => cn == actionColumn.ColumnName));
}
}
}
}
With the SubstitutionDataTable defined as below:
namespace Lib
{
public sealed class SubstitutionDataTable: INotifyPropertyChanged
{
private string _name;
/// <summary>
/// The <see cref="Name" /> property's name.
/// </summary>
private const string NamePropertyName = "Name";
public SubstitutionDataTable()
{
ColumnNames = new ObservableCollection<string>();
}
/// <summary>
/// Gets the Name property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string Name
{
get
{
return _name;
}
set
{
if (_name == value)
{
return;
}
_name = value;
RaisePropertyChanged(NamePropertyName);
}
}
public ObservableCollection<string> ColumnNames { get; set; }
#region Implementation of INotifyPropertyChanged
/// <summary>
/// A property has changed - update bindings
/// </summary>
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
...Now this is the crux of the puzzle...
The above classes are used to define a new DataTable within a DataSet and add columns and rows and run-time. I have another Class that allows configuration of an obfuscation process, part of the configuration allows selection of a DataTable and DataColumn from the SubstituionDataSet defined above.
namespace Lib
{
public class ObfuscationParams : INotifyPropertyChanged
{
private string _dataColumn;
private string _dataTable;
private char _maskCharacter;
private int _numberCharacters;
/// <summary>
/// The <see cref="MaskCharacter" /> property's name.
/// </summary>
private const string MaskCharacterPropertyName = "MaskCharacter";
/// <summary>
/// The <see cref="DataColumn" /> property's name.
/// </summary>
private const string DataColumnPropertyName = "DataColumn";
/// <summary>
/// The <see cref="DataTable" /> property's name.
/// </summary>
private const string DataTablePropertyName = "DataTable";
# region Mask Obfuscation Properties
/// <summary>
/// Defines whether whitespace is to be trimmed or not for a Mask obfuscation.
/// </summary>
public bool IsWhiteSpaceTrimmed { get; set; }
/// <summary>
/// Defines the mask character to be used for a Mask obfuscation.
/// </summary>
public char MaskCharacter
{
get { return _maskCharacter; }
set
{
if (_maskCharacter == value)
return;
_maskCharacter = value;
RaisePropertyChanged(MaskCharacterPropertyName);
}
}
/// <summary>
/// Defines the number of masking characters to apply.
/// </summary>
public int NumberCharacters
{
get { return _numberCharacters; }
set { _numberCharacters = value < 1 ? 1 : (value > 16 ? 16 : value); }
}
/// <summary>
/// Defines the mask position for a Mask obfuscation.
/// </summary>
public MaskPosition MaskPosition { get; set; }
#endregion
# region Substitute Obfuscation Properties
/// <summary>
/// Defines which datacolumn is to be used for a Substitution obfuscation.
/// </summary>
public string DataColumn
{
get { return _dataColumn; }
set
{
if (_dataColumn == value)
return;
_dataColumn = value;
RaisePropertyChanged(DataColumnPropertyName);
}
}
/// <summary>
/// Defines which datatable is to be used for a substitition obfuscation.
/// </summary>
public string DataTable
{
get { return _dataTable; }
set
{
if (_dataTable == value)
return;
_dataTable = value;
RaisePropertyChanged(DataTablePropertyName);
_dataTable = value;
}
}
#endregion
#region Implementation of INotifyPropertyChanged
/// <summary>
/// A property has changed - update bindings
/// </summary>
[field: NonSerialized]
public virtual event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
I have the configuration working and can configure a number of obfuscations and then serialize the configuration to disk.
When I deserialize I find the bindings on the GUI don't show the correct DataTable and DataColumn selections, the DataTable just shows the fully qualified object name.
I am currently just trying to get the DataTable binding working - I know I need to rework the DataColumn binding.
The GUI (usercontrol) is defined as below:
<UserControl xmlns:igEditors="http://infragistics.com/Editors" x:Class="SubstitutionOptions"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="421" d:DesignWidth="395">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="23" />
<RowDefinition Height="23" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0"
Grid.Column="0"
Text="Dataset" />
<igEditors:XamComboEditor Grid.Row="0"
Grid.Column="2"
Name="tablesComboBox"
NullText="select a dataset..."
ItemsSource="{Binding DataContext.Project.SubstitutionDataSet.TableNames, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
DisplayMemberPath="Name"
SelectedItem="{Binding DataContext.SelectedFieldSubstitutionDataTable, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
<TextBlock Grid.Row="1"
Grid.Column="0"
Text="Column" />
<igEditors:XamComboEditor Grid.Row="1"
Grid.Column="2"
NullText="select a column..."
ItemsSource="{Binding DataContext.SelectedFieldSubstitutionDataTable.ColumnNames, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
SelectedItem="{Binding DataColumn, Mode=TwoWay}"/>
</Grid>
</UserControl>
I hope I have explained the problem sufficiently. Has anyone got any ideas on how I can either get it working using the current design or redesign the approach to achieve what I need?
OK, think I've cracked it now, not that anyone seems interested :-)
I'll post the answer for posterity though...
I changed the bindings for the Comboboxes like so...
<TextBlock Grid.Row="0"
Grid.Column="0"
Text="Dataset" />
<igEditors:XamComboEditor Grid.Row="0"
Grid.Column="2"
NullText="select a dataset..."
ItemsSource="{Binding DataContext.VDOProject.SubstitutionDataSet.TableNames, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
DisplayMemberPath="Name"
Text="{Binding DataTable, Mode=TwoWay}"
SelectedItem="{Binding DataContext.SelectedFieldSubstitutionDataTable, Mode=OneWayToSource, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"/>
<TextBlock Grid.Row="1"
Grid.Column="0"
Text="Column" />
<igEditors:XamComboEditor Grid.Row="1"
Grid.Column="2"
NullText="select a column..."
ItemsSource="{Binding DataContext.SelectedFieldSubstitutionDataTable.ColumnNames, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
Text="{Binding DataColumn, Mode=TwoWay}" />

Categories