The binding on the chart:FastLineBitmapSeries which is part of the SyncFusion package does not work.
How to do it? I have another property that uses the BoolToVisibility converter that works, why not this one?
Here is my code:
Xaml
<chart:FastLineBitmapSeries DataContext="{Binding AllSeries[VT8PvPower]}" Interior="#7f84e8" VisibilityOnLegend="{Binding AllSeriesVisibility[VT10PvPower], Converter={StaticResource BoolToVisibility}, Mode=TwoWay}" />
The DataContext is working fine but not the VisibilityOnLegend
View model
AllSeriesVisibility propertie: (same as AllSeries)
public ObservableDictionary<string, bool> AllSeriesVisibility { get; set; } = new ObservableDictionary<string, bool>();
Content of the properties:
AllSeriesVisibility - Keys
Values:
the values are not always at true like in pic
Converter
public class VisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is bool bValue))
return Visibility.Hidden;
return bValue ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Change the binding modes / change the public property, I tried to put a public bool instead of an ObservableDictionnary but it still doesn't work
We have analyzed your code snippet, we suspect that the classes that contain the AllSeries and AllSeriesVisibility properties are the same.
The remaining binding properties for the series should be in the AllSeries value object when you specify BindingContext as the AllSeries Key object, not in the AllSeries parent class.
Based on your code snippet, we created a simple sample that functions properly when given the right class structure as shown below. Additionally, VisibilityonLegend will function flawlessly.
<chart:SfChart x:Name="Chart" Margin="10">
<chart:SfChart.Resources>
<local:VisibilityConverter x:Key="BoolToVisiblity"/>
</chart:SfChart.Resources>
. . .
<chart:FastLineBitmapSeries DataContext="{Binding AllSeries[Series1]}"
ItemsSource="{Binding ChartData}" Label="series1"
VisibilityOnLegend="{Binding AllSeriesVisibility[Series1],
Converter={StaticResource BoolToVisiblity}}"
XBindingPath="XValue" YBindingPath="YValue1" />
<chart:FastLineBitmapSeries DataContext="{Binding AllSeries[Series2]}"
ItemsSource="{Binding ChartData}" Label="series2"
VisibilityOnLegend="{Binding AllSeriesVisibility[Series2],
Converter={StaticResource BoolToVisiblity }}"
XBindingPath="XValue" YBindingPath="YValue2" />
. . .
</chart:SfChart>
public class ViewModel
{
public Dictionary<string, ViewModel1> AllSeries { get; set; } =
new Dictionary<string, ViewModel1>();
public string Series1Name { get; set; } = "Series1";
public string Series2Name { get; set; } = "Series2";
public ViewModel()
{
AllSeries["Series1"] = new ViewModel1(false);
AllSeries["Series2"] = new ViewModel1(true);
}
}
public class ViewModel1
{
private ObservableCollection<DataPoint> _chartData;
public ObservableCollection<DataPoint> ChartData
{
get { return _chartData; }
set { _chartData = value; }
}
public Dictionary<string, bool> AllSeriesVisibility { get; set; } = new Dictionary<string, bool>();
public ViewModel1(bool value)
{
AllSeriesVisibility["Series1"] = value;
AllSeriesVisibility["Series2"] = value;
var vTemp = new ObservableCollection<DataPoint>();
var random = new Random();
for (var i = 1; i < 15; i++)
{
vTemp.Add(new DataPoint { XValue = i, YValue1 = random.NextDouble(),
YValue2=random.NextDouble() });
}
ChartData = vTemp;
}
}
Please check this and let us know if you need any further assistance.
Regards,
Muneesh Kumar G
Related
I'm facing some issues with my C# WPF project and, especially, the DataGridComboBoxColumn control. So, here's a part of my MainWindow.xaml file :
<DataGrid x:Name="WorkDayGrid"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserSortColumns="False"
ItemsSource="{Binding}">
<DataGrid.Resources>
<local:Times x:Key="times" />
<local:TimeConverter x:Key="timeConverter" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Arrival Time"
ItemsSource="{StaticResource times}"
SelectedItemBinding="{Binding ArrivalTime, Converter={StaticResource timeConverter}}" />
</DataGrid.Columns>
</DataGrid>
And the corresponding "code-behind" :
public partial class MainWindow : Window
{
private ObservableCollection<WorkDay> _workDays = new ObservableCollection<WorkDay>();
public MainWindow()
{
InitializeComponent();
ComputeWorkDays();
WorkDayGrid.DataContext = _workDays;
}
private void ComputeWorkDays()
{
_workDays.Clear();
for (var i = 1; i <= 31; ++i)
{
var d = new WorkDay();
_workDays.Add(d);
}
}
}
The code of the WorkDay class :
class WorkDay
{
public Time ArrivalTime { get; set; }
}
The code of the Times class :
class Times : ObservableCollection<Time>
{
public Times()
{
var firstHour = 7;
var lastHour = 20;
var minuteStep = 5;
for (var i = firstHour; i < lastHour; ++i)
{
for (var j = 0; j < 60; j += minuteStep)
{
var t = new Time
{
Hour = i,
Minute = j
};
Add(t);
}
}
}
}
The code of the Time class :
class Time
{
private int _hour = 0;
private int _minute = 0;
public int Hour
{
set => _hour = value;
}
public int Minute
{
set => _minute = value;
}
public override string ToString()
{
var fmt = "D2";
return _hour.ToString(fmt) + ":" + _minute.ToString(fmt);
}
}
The code of the TimeConverter class :
class TimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
var time = (Time)value;
return time.ToString();
}
else
{
string result = "";
return result;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (Time)value;
}
}
The problem is when I select a value in one cell (thanks to the related combo box), the same value is also displayed in another cell. In addition, when I scroll down to another row in the data grid, the selected value disappears. I wonder if the DataGridComboBoxColumn works well... I use the .NET framework 4.6.1. Thank you very much for your help.
At the end of the day, it was the binding converter which caused the issue. Thank you #Clemens for your help.
FYI, I don't use the DateTime struct because I need an "invalid" time to allow the user to cancel its selection.
I have a class shown below.
class RegionSale
{
DateTime DateSale;
string Region;
double DollarAmount;
}
At run time the code wouldn't know how many RegionSale objects would be required, this is fine as I can just create List of RegionSale objects.
The problem I have is I'm now be asking to display the data via a wpf datagrid but in the format shown below,
DateS UK US EUxUK JAP Brazil
2015-12-03 23634 22187 NULL NULL NULL
2015-12-04 56000 22187 NULL NULL NULL
2015-12-14 56000 10025 NULL NULL NULL
So I could create a new class like below (however feel this is a bad idea)
class RegionSaleNew
{
DateTime DateSale;
double UK;
double US;
double EUxUK;
double JAP;
double Brazil;
}
As I mentioned earlier I won't know at runtime the number of regions so the class above seems like a bad idea, however its obviously easy to bind to the datagrid.
The question is how best to structure my class bearing in mind the format of the datagrid & without knowing the number of regions until runtime? Is reflection a good idea?
I would write a class that derives the INotifyPropertyChanged interface and create a property for every region like in your example.
Then I would use a ObservableCollection<myClass> and bind to the properties.
class RegionSaleNew : INotifyPropertyChanged
{
DateTime DateSale;
double _uk;
.....
public double UK
{
get { return _monitor; }
set
{
if (value == _uk) return;
_uk = value;
OnPropertyChanged();
}
}
// Add all properties here
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
In XAML you could then bind like this
...
<DataGridTextColumn Width="Auto" Header="UK" Binding="{Binding UK, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True"/>
...
All you have to do then is to set the DataGrid.ItemsSource = myObservableCollection or in XAML <DataGrid ItemsSource="{Binding myObservableCollection} ... </DataGrid>
Actually I was interested in the same subject, so it was the target of the my first SO question Cross tabular data binding in WPF. The provided answer there covered it in general, but not for the DataGrid specific binding. So my conclusion was that the solution should be based on some System.ComponentModel concepts:
(1) Custom PropertyDescriptor implementation for providing "virtual" properties.
(2) The item class implementing ICustomTypeDescriptor to expose the "virtual" properties per item
(3) The collection class implementing ITypedList to allow automatic data grid column creation from the "virtual" properties.
Having your model
class RegionSale
{
public DateTime DateSale;
public string Region;
public double DollarAmount;
}
and data
IEnumerable<RegionSale> data = ...;
the "virtual" properties can be determined at runtime by simply getting a distinct list of the Region field:
var regions = data.Select(sale => sale.Region).Distinct().ToList();
For each region we'll create a property descriptor with region as Name, which later will be used as key into item internal dictionary to retrieve the value.
The items will be built by grouping the data by DateSale.
Here is the whole implementation:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
class RegionSalePivotViewItem : CustomTypeDescriptor
{
private RegionSalePivotView container;
private Dictionary<string, double> amountByRegion;
internal RegionSalePivotViewItem(RegionSalePivotView container, DateTime date, IEnumerable<RegionSale> sales)
{
this.container = container;
DateSale = date;
amountByRegion = sales.ToDictionary(sale => sale.Region, sale => sale.DollarAmount);
}
public DateTime DateSale { get; private set; }
public double? GetAmount(string region)
{
double value;
return amountByRegion.TryGetValue(region, out value) ? value : (double?)null;
}
public override PropertyDescriptorCollection GetProperties()
{
return container.GetItemProperties(null);
}
}
class RegionSalePivotView : ReadOnlyCollection<RegionSalePivotViewItem>, ITypedList
{
private PropertyDescriptorCollection properties;
public RegionSalePivotView(IEnumerable<RegionSale> source) : base(new List<RegionSalePivotViewItem>())
{
// Properties
var propertyList = new List<PropertyDescriptor>();
propertyList.Add(new Property<DateTime>("DateSale", (item, p) => item.DateSale));
foreach (var region in source.Select(sale => sale.Region).Distinct().OrderBy(region => region))
propertyList.Add(new Property<double?>(region, (item, p) => item.GetAmount(p.Name)));
properties = new PropertyDescriptorCollection(propertyList.ToArray());
// Items
((List<RegionSalePivotViewItem>)Items).AddRange(
source.GroupBy(sale => sale.DateSale,
(date, sales) => new RegionSalePivotViewItem(this, date, sales))
.OrderBy(item => item.DateSale)
);
}
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) { return properties; }
public string GetListName(PropertyDescriptor[] listAccessors) { return null; }
class Property<T> : PropertyDescriptor
{
Func<RegionSalePivotViewItem, Property<T>, T> getValue;
public Property(string name, Func<RegionSalePivotViewItem, Property<T>, T> getValue) : base(name, null) { this.getValue = getValue; }
public override Type ComponentType { get { return typeof(RegionSalePivotViewItem); } }
public override Type PropertyType { get { return typeof(T); } }
public override object GetValue(object component) { return getValue((RegionSalePivotViewItem)component, this); }
public override bool IsReadOnly { get { return true; } }
public override bool CanResetValue(object component) { return false; }
public override void ResetValue(object component) { throw new NotSupportedException(); }
public override void SetValue(object component, object value) { throw new NotSupportedException(); }
public override bool ShouldSerializeValue(object component) { return false; }
}
}
Sample test:
ViewModel:
class ViewModel
{
public RegionSalePivotView PivotView { get; set; }
}
XAML:
<Window x:Class="WpfApplication1.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:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid x:Name="dataGrid" HorizontalAlignment="Left" Margin="28,33,0,0" VerticalAlignment="Top" Height="263" Width="463" ItemsSource="{Binding PivotView}"/>
</Grid>
</Window>
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var data = new[]
{
new RegionSale { DateSale = new DateTime(2015, 12, 03), Region = "UK", DollarAmount = 23634 },
new RegionSale { DateSale = new DateTime(2015, 12, 03), Region = "US", DollarAmount = 22187 },
new RegionSale { DateSale = new DateTime(2015, 12, 04), Region = "UK", DollarAmount = 56000 },
new RegionSale { DateSale = new DateTime(2015, 12, 04), Region = "US", DollarAmount = 22187 },
new RegionSale { DateSale = new DateTime(2015, 12, 14), Region = "UK", DollarAmount = 56000 },
new RegionSale { DateSale = new DateTime(2015, 12, 14), Region = "US", DollarAmount = 10025 },
};
DataContext = new ViewModel { PivotView = new RegionSalePivotView(data) };
}
}
Result:
I try to find solution to output localized enums from resources using Binding.
Now I bind enums by common way like that:
<Page.Resources>
<ObjectDataProvider x:Key="RootConverterType" MethodName="GetValues" ObjectType="{x:Type sys:Enum}" >
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="settingsManager:RootConverterType"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ComboBox ItemsSource="{Binding Source={StaticResource RootConverterType}}" SelectedValue="{Binding Path=CameraPosition.Config.UI.ValueConverterType.W, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
This is not localized enums, but I wish to use localization for them (using different languages from resources) and with conversion from localized string to enum in background without ComboBox events and explicit conversion. Is this possible? If yes, could someone provide simple code example please?
I think it is impossible if you import multi xaml files to implement localization.
Because if you import languages to xaml , they are static resources. I suggested you use Binding dynamic resource, and import resources in cs files to initialize resource key.
Xaml Like this:
Content="{DynamicResource UID_AppCommon_MiniPA_Close}"
CS Like this:
this.Resources.MergedDictionaries.Add(your resource file);
I'm using a wrapper struct to solve this problem:
public enum AttributeType {
Bool,
Number,
String
}//AttributeType
public struct AttributeTypeWrapper {
public AttributeTypeWrapper(AttributeType type) {
this.type = type;
}
private AttributeType type;
public AttributeType Type {
get {
return type;
}
set {
type = value;
}
}
public override string ToString() {
switch(type) {
case AttributeType.Bool:
return Properties.Resources.txtBool;
case AttributeType.Number:
return Properties.Resources.txtNumber;
case AttributeType.String:
return Properties.Resources.txtString;
default:
return "Invalid AttributeType";
}
}
}// AttributeTypeWrapper
Note that it is a struct not a class. So it is a value type and can easyly set as SelectedItem of a ComboBox or ListBox for example.
To go a step further, you can implement an IValueConverte for simple Binding:
/// <summary>
/// Convert a AttributeType into its wrapper class to display strings from resources
/// in the selected language
/// </summary>
[ValueConversion(typeof(AttributeType), typeof(AttributeTypeWrapper))]
public class AttributeTypeToWrapperConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return new AttributeTypeWrapper((AttributeType)value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return ((AttributeTypeWrapper)value).Type;
}
}
Then you can bind SelectedItem directly to the enum type:
<AttributeTypeToWrapperConverter x:Key="convertAttrTypeToWrapper"/>
<ComboBox ItemsSource="{Binding Path=DataTypes}"
SelectedItem="{Binding Path=SelectedDataType, Converter={StaticResource convertAttrTypeToWrapper}}"/>
DataTypes is an Array of AttributeTypeWrapper structs. SelectedDataType is of type
AttributeType. (You can convert the ItemsSource as well).
This works pretty fine for me.
I found another way to localize enums:
Here is my class which you can use ase an example:
ru_RU and en_US - resources file names.
public class EnumLocalizationManager : BindableObject
{
public Language language;
private CommonLocalization commonLang;
private ObservableCollection rootCoverterTypes;
public EnumLocalizationManager()
{
commonLang = CommonLocalization.GetInstance;
EnumLanguage = commonLang.Lang;
}
//Коллекция для локализации enum RootConverterType
public static Dictionary<Language, ObservableCollection<string>> RootConverterLocalization = new Dictionary<Language, ObservableCollection<string>>()
{
{
Language.ru_RU, new ObservableCollection<string>()
{
ru_RU.CameraEnumConverterTypeUndefined, ru_RU.CameraEnumConverterTypeAuto, ru_RU.CameraEnumConverterTypeNumber, ru_RU.CameraEnumConverterTypeExponent, ru_RU.CameraEnumConverterTypeDecimal, ru_RU.CameraEnumConverterTypeInteger
}
},
{
Language.en_US, new ObservableCollection<string>()
{
en_US.CameraEnumConverterTypeUndefined, en_US.CameraEnumConverterTypeAuto, en_US.CameraEnumConverterTypeNumber, en_US.CameraEnumConverterTypeExponent, en_US.CameraEnumConverterTypeDecimal, en_US.CameraEnumConverterTypeInteger
}
}
};
//Коллекция для локализации enum ConverterType
public static Dictionary<Language, ObservableCollection<string>> ConverterLocalization = new Dictionary<Language, ObservableCollection<string>>()
{
{
Language.ru_RU, new ObservableCollection<string>()
{
ru_RU.CameraEnumConverterTypeAuto, ru_RU.CameraEnumConverterTypeNumber, ru_RU.CameraEnumConverterTypeExponent, ru_RU.CameraEnumConverterTypeDecimal, ru_RU.CameraEnumConverterTypeInteger
}
},
{
Language.en_US, new ObservableCollection<string>()
{
en_US.CameraEnumConverterTypeAuto, en_US.CameraEnumConverterTypeNumber, en_US.CameraEnumConverterTypeExponent, en_US.CameraEnumConverterTypeDecimal, en_US.CameraEnumConverterTypeInteger
}
}
};
public ObservableCollection<string> RootConverterTypes
{
get { return rootCoverterTypes; }
}
public ObservableCollection<string> ConverterTypes
{
get { return coverterTypes; }
}
public Language EnumLanguage
{
get { return language; }
set
{
language = value;
ChangeEnumLanguage();
}
}
private void ChangeEnumLanguage()
{
if (RootConverterLocalization.ContainsKey(language))
{
rootCoverterTypes = RootConverterLocalization[language];
}
if (ConverterLocalization.ContainsKey(language))
{
coverterTypes = ConverterLocalization[language];
}
RaisePropertyChanged();
RaisePropertyChangedByName("RootConverterTypes");
RaisePropertyChangedByName("ConverterTypes");
}
}
}
BindableObject class is class which incapsulates INotifyPropertyChanged.
First of all - your enums must be numbered (it is needed for ValueConverter)
for ex:
public enum ConverterType
{
Auto = 0,
Number = 1,
Exponential = 2,
Decimal = 3,
Integer = 4
}
public enum RootConverterType
{
Undefined = 0,
Auto = 1,
Number = 2,
Exponential = 3,
Decimal = 4,
Integer = 5
}
and the last part - ValueConvert by itself:
class EnumCameraVariantToLocalizedStringConverter:ConverterBase
{
public EnumCameraVariantToLocalizedStringConverter()
{
}
public override object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (int)(CameraVariant)value;
}
public override object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int index = (int)value;
switch (index)
{
case 0:
return CameraVariant.Undefined;
case 1:
return CameraVariant.FirstPerson;
case 2:
return CameraVariant.ThirdPerson;
case 3:
return CameraVariant.Flight;
}
return index;
}
}
I use inheritance from base class just to use makrup extenstions without adding resources for each converter.
And the Binding itself:
<ComboBox Style="{StaticResource CameraMainSelectorStyle}"
ItemsSource="{Binding Source={StaticResource EnumLocalizationManager}, Path=CameraVariant}"
SelectedIndex="{Binding Path=CameraSettingsManager.StartUpCameraModeFilter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={valueConverters:EnumCameraVariantToLocalizedStringConverter}}"
Tag="{Binding Path=CameraSettingsManager.StartUpCameraModeFilter, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
SelectionChanged="StartUpCameraTypeFilter_OnSelectionChanged"/>
Here is binding enum to Combobox. I hope evething is clear. One thing here. If you want to change language on fly, you must add some code for not loosing selected item after language changing:
if (((ComboBox)sender).SelectedIndex < 0)
{
if (((ComboBox) sender).Tag != null)
{
CameraVariant behavior = (CameraVariant) ((ComboBox) sender).Tag;
((ComboBox) sender).SelectedIndex = (int) behavior;
}
}
That all. Looks a little bit scary, but there is nothing hard.
I'm new to WPF and I have some difficulties when I'm trying to populate a ListView with a list of custom objects.
internal class ApplicationCode
{
public int Code { get; set; }
public IEnumerable<string> InstrumentCodes { get; set; }
}
I have a list of ApplicationCode which I set to ItemsSource to a ListView. I need to display the ApplicationCode.Code as a string and for the rest of the columns a check box which can be checked/unchecked depending if the column name is contained in the InstrumentCodes collection.
In order to set the check box I use a converter on databinding:
<DataTemplate x:Key="InstrumentCodeTemplate">
<CheckBox IsEnabled="False" IsChecked="{Binding Mode=OneTime, Converter={StaticResource InstrumentSelectionConverter}}" />
</DataTemplate>
The problem I have is because I can't know which is the current column at the time of cell data binding and I can't set the ConverterParameter.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ApplicationCode appCode = value as ApplicationCode;
return appCode != null && appCode.InstrumentCodes.Contains(parameter.ToString());
}
Small example:
Id | Code1 | Code3 | Code4
--------------------------------
123 | True | False | True
Data for row 1: ApplicationCode.InstrumentCodes {Code1, Code4}
There is a way to find out the column index or name? Or there is another way to solve this problem?
The column name should be nothing more then a visual; which means the needed data should all be residing in the underlying object model. Therefore each row of data is an object.
Perhaps a restructure of your code would suffice which would also remove the need for the converter...keep in mind this is an example to get the idea across and would need modified for actual use.
internal class ApplicationCode
{
private CodeService _codeService = new CodeService();
public int Code { get; set; }
public bool IsValidCode
{
get
{
return _codeService.DoesIntrumentCodeExist(Code.ToString());
}
}
}
internal class CodeService
{
private IEnumerable<string> _instrumentCodes;
public CodeService()
{
//instantiate this in another way perhaps via DI....
_instrumentCodes = new List<string>();
}
public bool DoesIntrumentCodeExist(String instrumentCode)
{
foreach (String code in _instrumentCodes)
{
if (code == instrumentCode)
return true;
}
return false;
}
}
The solution I got so far is to add the columns dynamically and to set the ConverterParameter on every column.
foreach (var instrument in instruments)
{
var column = new GridViewColumn
{
HeaderTemplate = GetColumnHeaderTemplate(instrument),
CellTemplate = GetColumnCellTemplate(instrument),
Header = instrument,
};
view.Columns.Add(column);
}
private static DataTemplate GetColumnCellTemplate(string instrument)
{
var binding = new Binding
{
ConverterParameter = instrument,
Converter = new InstrumentSelectionConverter(),
Mode = BindingMode.OneWay
};
var template = new DataTemplate();
template.VisualTree = new FrameworkElementFactory(typeof(CheckBox));
template.VisualTree.SetBinding(ToggleButton.IsCheckedProperty, binding);
return template;
}
I know this isn't the best solution and I would really appreciate if someone could show me a way to do this directly from .xaml.
Im struggling with Observable collections and using it to add pushpins onto a silverlight bing map. Im trying to build up a collection here using Linq. But im getting the error under every "PushPinItems" instance in my code saying:
'observable_collection_test.Map.PushPinItems' is a 'field' but is used like a 'type' c:\users\dan\documents\visual studio 2010\Projects\observable collection test\observable collection test\Map.xaml.cs 26 38 observable collection test
Not sure whats going on here, am I declaring/constructing it wrong or something?
Im new to Observable collections (and most of c#!) so any help/advice welcome. Many thanks.
UPDATE:
This seems to be ok now, the above issue, but now its not binding my items to pushpins.
I have looked at the "PushPins = pushPinCollection;" method and all 143 items are in there with lat, long and location propertiess with the correct data- as per this breakpoint:
Maybe there is an issue with my XAML binding?
Here is the updated code:
namespace observable_collection_test
{
public partial class Map : PhoneApplicationPage
{
private ObservableCollection<SItem2> _PushPins;
public event PropertyChangedEventHandler PropertyChanged;
public Map()
{
InitializeComponent();
getItems();
}
public ObservableCollection<SItem2> PushPins
{
get
{
return _PushPins;
}
private set
{
_PushPins = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("PushPins"));
}
}
}
private GeoCoordinate _location;
public GeoCoordinate Location
{
get { return _location; }
set
{
if (_location != value)
{
_location = value;
}
}
}
private string _pinSource;
public string PinSource
{
get { return _pinSource; }
set
{
if (_pinSource != value)
{
_pinSource = value;
}
}
}
public void getItems()
{
var document = XDocument.Load("ListSmall.xml");
if (document.Root == null)
return;
var xmlns = XNamespace.Get("http://www.blahblah.co.uk/blah");
var events = from ev in document.Descendants("item")
select new
{
Latitude = Convert.ToDouble(ev.Element(xmlns + "Point").Element(xmlns + "lat").Value),
Longitude = Convert.ToDouble(ev.Element(xmlns + "Point").Element(xmlns + "long").Value),
};
ObservableCollection<SItem2> pushPinCollection = new ObservableCollection<SItem2>();
foreach (var ev in events)
{
SItem2 PushPin = new SItem2
( ev.Latitude, ev.Longitude)
{
};
pushPinCollection.Add(PushPin);
}
PushPins = pushPinCollection;
}
other class:
namespace observable_collection_test
{
public class SItem2
{
//public DateTimeOffset Date { get; set; }
//public string Title
//{ get; set; }
public double Latitude
{ get; set; }
public double Longitude
{ get; set; }
public GeoCoordinate Location
{ get; set; }
//public Uri Link { get; set; }
public SItem2(//string Title,
double Latitude, double Longitude)
{
//this.Date = Date;
//this.Title = Title;
this.Latitude = Latitude;
this.Longitude = Longitude;
//this.Location = Location;
//this.Link = Link;
}
}
Bit of XAML concerning adding pins to map:
<my:Map ZoomBarVisibility="Visible" ZoomLevel="10" CredentialsProvider="AhqTWqHxryix_GnWER5WYH44tFuutXNEPvFm5H_CvsZHQ_U7-drCdRDvcWSNz6aT" Height="508" HorizontalAlignment="Left" Margin="0,22,0,0" Name="map1" VerticalAlignment="Top" Width="456">
<my:MapItemsControl ItemsSource="{Binding PushPins}" >
<my:MapItemsControl.ItemTemplate>
<DataTemplate>
<my:Pushpin Background="Aqua" Location="{Binding Location}" ManipulationCompleted="pin_click">
</my:Pushpin>
</DataTemplate>
</my:MapItemsControl.ItemTemplate>
</my:MapItemsControl>
</my:Map>
It would also be good to know if I am approaching the pushpin binding to the maps in the right way.
It looks as if this is because you have used x:Name="PushPinItems" in your XAML which is the same name as one of your types, so when you think you are referencing your PushPinItems type in your codebehind, you are actually referencing the field that VS has generated for you from your XAML that represents that Pushpin instance. You could use a different x:Name in your XAML.
Update
Ok, I see the issue :) I haven't worked with the Bing maps control before, but looking at http://forums.silverlight.net/forums/t/197631.aspx (second post down), you need to set the map controls MapItemsControl property. The ItemsSource property here should be bound to your ObservableCollection of a custom type which contains properties such as Name and Location. You can then populate this collection with instances of this custom type (in the post they have used MapData as the type name).
You can also get more examples and source code at http://www.microsoft.com/maps/isdk/silverlight/