I created a reference to load big data into a datagrid with a dapper extension. I have a Behavior that detects when the scroll is then down load the following data using this RelayCommand:
XAML in Datagrid Properties :
Behavior:ScrollViewerMonitor.AtEndCommand="{Binding LoadCommand}"
My Behavior (Detect when scroll is down) :
public class ScrollViewerMonitor
{
public static DependencyProperty AtEndCommandProperty
= DependencyProperty.RegisterAttached(
"AtEndCommand", typeof(ICommand),
typeof(ScrollViewerMonitor),
new PropertyMetadata(OnAtEndCommandChanged));
public static ICommand GetAtEndCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(AtEndCommandProperty);
}
public static void SetAtEndCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(AtEndCommandProperty, value);
}
public static void OnAtEndCommandChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = (FrameworkElement)d;
if (element != null)
{
element.Loaded -= element_Loaded;
element.Loaded += element_Loaded;
}
}
static void element_Loaded(object sender, RoutedEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
element.Loaded -= element_Loaded;
ScrollViewer scrollViewer = FindChildOfType<ScrollViewer>(element);
if (scrollViewer == null)
{
// throw new InvalidOperationException("ScrollViewer not found.");
return;
}
var dpd = DependencyPropertyDescriptor.FromProperty(ScrollViewer.VerticalOffsetProperty, typeof(ScrollViewer));
dpd.AddValueChanged(scrollViewer, delegate (object o, EventArgs args)
{
bool atBottom = scrollViewer.VerticalOffset
>= scrollViewer.ScrollableHeight;
if (atBottom)
{
var atEnd = GetAtEndCommand(element);
if (atEnd != null)
{
atEnd.Execute(null);
}
}
});
}
static T FindChildOfType<T>(DependencyObject root) where T : class
{
var queue = new Queue<DependencyObject>();
queue.Enqueue(root);
while (queue.Count > 0)
{
DependencyObject current = queue.Dequeue();
for (int i = VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--)
{
var child = VisualTreeHelper.GetChild(current, i);
var typedChild = child as T;
if (typedChild != null)
{
return typedChild;
}
queue.Enqueue(child);
}
}
return null;
}
}
My ViewModel with LoadCommand :
//Init mmy ObservableCollection for DataGrid
var myObservableCollection = new ObservableCollection<Mouvement_Brouillard>();
//Init my Object
//Parameters(NumberPerPage,Conditions,OrderBy,Connection)
var myReference = new Paged2<Mouvement_Brouillard>(150, "", "Swmo_Id", new ConnectionProvider());
//Load First Datas
myReference.AddDatas(myObservableCollection);
//Call LoadCommand when Scroll is down
LoadCommand = new RelayCommand<object>(myReference.LoadCommand(myObservableCollection));
And my reference Paged2 (AddData in ObservableCollection and execute LoadCommand:
public class Paged2<T>
{
private T Value { get; set; }
public int NumberPage { get; set; }
public int RowsPerPage { get; set; }
public string Conditions { get; set; }
public string OrderBy { get; set; }
public ConnectionProvider Cnn { get; set; }
public static bool Busy;
public Paged2(int _RowsPerPage, string _Conditions, string _OrdeBy, ConnectionProvider _Cnn)
{
this.RowsPerPage = _RowsPerPage;
this.Conditions = _Conditions;
this.OrderBy = _OrdeBy;
this.NumberPage = 1;
this.Cnn = _Cnn;
}
public async void AddDatas(ObservableCollection<T> myList)
{
IEnumerable<T> myNewBlocList;
//DAL
using (var myCnn = this.Cnn.GetOpenConnection())
{
myNewBlocList = await myCnn.GetListPagedAsync<T>(this.NumberPage, this.RowsPerPage, this.Conditions, this.OrderBy);
}
NumberPage++;
foreach (var Item in myNewBlocList)
myList.Add(Item);
}
public Action<object> LoadCommand(Ref<ObservableCollection<T>> myList)
{
return new Action<object>(
obj =>
{
if (Busy)
return;
Busy = true;
System.Threading.ThreadPool.QueueUserWorkItem(
delegate
{
Application.Current.Dispatcher.BeginInvoke(new Action(
delegate
{
AddDatas(myList);
Busy = false;
}));
});
});
}
public class Ref<T>
{
public Ref() { }
public Ref(T value) { Value = value; }
public T Value { get; set; }
public override string ToString()
{
T value = Value;
return value == null ? "" : value.ToString();
}
public static implicit operator T(Ref<T> r) { return r.Value; }
public static implicit operator Ref<T>(T value) { return new Ref<T>(value); }
}
}
Everything works but since I outsource (place in another file ) method LoadCommand the next part of the code no longer works :
public Action<object> LoadCommand(Ref<ObservableCollection<T>> myList)
{
return new Action<object>(
obj =>
{
if (Busy)
return;
Busy = true;
System.Threading.ThreadPool.QueueUserWorkItem(
delegate
{
Application.Current.Dispatcher.BeginInvoke(new Action(
delegate
{
AddDatas(myList);
Busy = false;
}));
});
});
}
Related
I want to use Dynamic Data as WPF's CompositeCollection alternative in an AvaloniaUI project.
Here is some code to expose the issue :
public class MainWindowViewModel : ViewModelBase
{
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBind;
public ReadOnlyObservableCollection<ViewModelBase> TestBind => _testBind;
public IObservable<IChangeSet<ViewModelBase>> SeveralListTypes { get; set; }
public IObservable<IChangeSet<ViewModelBase>> SeveralListTypes2 { get; set; }
public IObservable<IChangeSet<ViewModelBase>> SeveralListTypes3 { get; set; }
public ObservableCollection<ViewModelBase> ListTypesObject1 { get; set; }
public ObservableCollection<ViewModelBase> ListTypesObject2 { get; set; }
public ObservableCollection<ViewModelBase> ListTypesObject3 { get; set; }
public IObservable<IChangeSet<ViewModelBase>> InBoth { get; set; }
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBindTypes;
public ReadOnlyObservableCollection<ViewModelBase> TestBindTypes => _testBindTypes;
public MainWindowViewModel()
{
// TODO : those object collections should be of the real type and not from ancestor
// ListTypesObject1 = new ObservableCollection<Object1>()
ListTypesObject1 = new ObservableCollection<ViewModelBase>()
{
new Object1(),
};
ListTypesObject2 = new ObservableCollection<ViewModelBase>()
{
new Object2(),
};
ListTypesObject3 = new ObservableCollection<ViewModelBase>()
{
new Object3(),
};
// Change observableCollection to IObservable to be running with engine ReactiveUI
SeveralListTypes = ListTypesObject1.ToObservableChangeSet();
SeveralListTypes2 = ListTypesObject2.ToObservableChangeSet();
SeveralListTypes3 = ListTypesObject3.ToObservableChangeSet();
//Group All Observable into one with Or operator
InBoth = SeveralListTypes.Or(SeveralListTypes2).Or(SeveralListTypes3);
// Bind => output to Binded Property for xaml
// Subscribe => to be notified when changes
var t = InBoth.Bind(out _testBindTypes)
.DisposeMany()
.Subscribe();
}
public void AddObject1()
{
var obj1 = new Object1("Added Object 1");
ListTypesObject1.Add(obj1);
}
public void AddObject2()
{
var obj2 = new Object2("Added Object 2");
ListTypesObject2.Add(obj2);
}
public void AddObject3()
{
if (ListTypesObject3 == null)
return;
var obj3 = new Object3("Added Object 3");
ListTypesObject3.Add(obj3);
}
public void DeleteObject1()
{
if(ListTypesObject1 != null && ListTypesObject1.Count > 0)
ListTypesObject1.RemoveAt(0);
}
public void DeleteObject2()
{
if (ListTypesObject2 != null && ListTypesObject2.Count > 0)
ListTypesObject2.RemoveAt(0);
}
public void DeleteObject3()
{
if (ListTypesObject3 != null && ListTypesObject3.Count > 0)
ListTypesObject3.RemoveAt(0);
}
public void DeleteObjectClear()
{
if (ListTypesObject3 == null)
return;
ListTypesObject3.Clear();
ListTypesObject3 = null;
ListTypesObject3 = new ObservableCollection<ViewModelBase>()
{
new Object3("Added object 3 from new list 3"),
};
SeveralListTypes3 = ListTypesObject3.ToObservableChangeSet();
InBoth = InBoth.Or(SeveralListTypes3);
// TODO : the collection we want to remove is still binded, the new one is not
}
public void DeleteObject3List()
{
if (ListTypesObject3 == null)
return;
ListTypesObject3.Clear();
ListTypesObject3 = null;
// TODO : remove the Object3List from DynamicData
}
public void CreateObject3List()
{
if (ListTypesObject3 != null)
return;
ListTypesObject3 = new ObservableCollection<ViewModelBase>()
{
new Object3("Added object 3 from new list 3"),
};
SeveralListTypes3 = ListTypesObject3.ToObservableChangeSet();
InBoth = InBoth.Or(SeveralListTypes3);
// TODO : the collection we want to remove is still binded, the new one is not
}
}
Object1,Object2 and Object3 are an heritage from ViewModelBase.
DeleteObjectClear method remove all Object3 from the binding but then the new list isn't displayed.
How to add or remove an ObservableCollection and refresh the binded object (TestBind) ?
As a second issue, is it possible to use the real type of object in ObservableCollection (with a common ancestor) instead of ViewModelBase and still use Dynamic Data to agregate all collections ?
Here is the full github repository POC to spotlight the issue : https://github.com/Whiletru3/pocbindingdmo
Thanks
I can't really do it only with DynamicData, so I came with this solution.
Not really elegant but it works...
I created an ObservableCollectionAggregate class. I can Assign and Unassign the different (typed) ObservableCollections.
public class ObservableCollectionAggregate : ObservableCollection<ViewModelBase>
{
private ObservableCollection<Object1> _subCollection1;
private ObservableCollection<Object2> _subCollection2;
private ObservableCollection<Object3> _subCollection3;
public ObservableCollectionAggregate()
{
_subCollection1 = null;
_subCollection2 = null;
_subCollection3 = null;
}
public void UnassignCollectionObject1()
{
if (_subCollection1 != null)
{
RemoveItems(_subCollection1);
_subCollection1.CollectionChanged -= OnSubCollectionChanged;
_subCollection1 = null;
}
}
public void AssignCollectionObject1(ObservableCollection<Object1> collection)
{
if (_subCollection1 != null)
{
UnassignCollectionObject1();
}
_subCollection1 = collection;
AddItems(_subCollection1);
_subCollection1.CollectionChanged += OnSubCollectionChanged;
}
public void UnassignCollectionObject2()
{
if (_subCollection2 != null)
{
RemoveItems(_subCollection2);
_subCollection2.CollectionChanged -= OnSubCollectionChanged;
_subCollection2 = null;
}
}
public void AssignCollectionObject2(ObservableCollection<Object2> collection)
{
if (_subCollection2 != null)
{
UnassignCollectionObject2();
}
_subCollection2 = collection;
AddItems(_subCollection2);
_subCollection2.CollectionChanged += OnSubCollectionChanged;
}
public void UnassignCollectionObject3()
{
if (_subCollection3 != null)
{
RemoveItems(_subCollection3);
_subCollection3.CollectionChanged -= OnSubCollectionChanged;
_subCollection3 = null;
}
}
public void AssignCollectionObject3(ObservableCollection<Object3> collection)
{
if (_subCollection3 != null)
{
UnassignCollectionObject3();
}
_subCollection3 = collection;
AddItems(_subCollection3);
_subCollection3.CollectionChanged += OnSubCollectionChanged;
}
private void AddItems(IEnumerable<ViewModelBase> items)
{
foreach (ViewModelBase me in items)
Add(me);
}
private void RemoveItems(IEnumerable<ViewModelBase> items)
{
foreach (ViewModelBase me in items)
Remove(me);
}
private void OnSubCollectionChanged(object source, NotifyCollectionChangedEventArgs args)
{
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
AddItems(args.NewItems.Cast<ViewModelBase>());
break;
case NotifyCollectionChangedAction.Remove:
RemoveItems(args.OldItems.Cast<ViewModelBase>());
break;
case NotifyCollectionChangedAction.Replace:
RemoveItems(args.OldItems.Cast<ViewModelBase>());
AddItems(args.NewItems.Cast<ViewModelBase>());
break;
case NotifyCollectionChangedAction.Move:
throw new NotImplementedException();
case NotifyCollectionChangedAction.Reset:
if (source is ObservableCollection<Object1>)
{
RemoveItems(this.Where(c => c is Object3).ToList());
}
if (source is ObservableCollection<Object2>)
{
RemoveItems(this.Where(c => c is Object3).ToList());
}
if (source is ObservableCollection<Object3>)
{
RemoveItems(this.Where(c => c is Object3).ToList());
}
break;
}
}
}
then I can use it in my viewmodel using DynamicData for the binding :
public class MainWindowViewModel : ViewModelBase
{
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBind;
public ReadOnlyObservableCollection<ViewModelBase> TestBind => _testBind;
public ObservableCollectionAggregate AggregatedCollection { get; set; }
public IObservable<IChangeSet<ViewModelBase>> AggregatedChangeSetFull { get; set; }
public ObservableCollection<Object1> ListTypesObject1 { get; set; }
public ObservableCollection<Object2> ListTypesObject2 { get; set; }
public ObservableCollection<Object3> ListTypesObject3 { get; set; }
private readonly ReadOnlyObservableCollection<ViewModelBase> _testBindTypes;
public ReadOnlyObservableCollection<ViewModelBase> TestBindTypes => _testBindTypes;
public MainWindowViewModel()
{
ListTypesObject1 = new ObservableCollection<Object1>()
{
new Object1(),
};
ListTypesObject2 = new ObservableCollection<Object2>()
{
new Object2(),
};
AggregatedCollection = new ObservableCollectionAggregate();
AggregatedCollection.AssignCollectionObject1(ListTypesObject1);
AggregatedCollection.AssignCollectionObject2(ListTypesObject2);
AggregatedChangeSetFull = AggregatedCollection.ToObservableChangeSet();
// Bind => output to Binded Property for xaml
// Subscribe => to be notified when changes
var t = AggregatedChangeSetFull
.DisposeMany()
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(out _testBindTypes)
.Subscribe();
}
public void AddObject1()
{
if (ListTypesObject1 == null)
return;
var obj1 = new Object1("Added Object 1");
ListTypesObject1.Add(obj1);
}
public void AddObject2()
{
if (ListTypesObject2 == null)
return;
var obj2 = new Object2("Added Object 2");
ListTypesObject2.Add(obj2);
}
public void AddObject3()
{
if (ListTypesObject3 == null)
return;
var obj3 = new Object3("Added Object 3");
ListTypesObject3.Add(obj3);
}
public void DeleteObject1()
{
if(ListTypesObject1 != null && ListTypesObject1.Count > 0)
ListTypesObject1.RemoveAt(0);
}
public void DeleteObject2()
{
if (ListTypesObject2 != null && ListTypesObject2.Count > 0)
ListTypesObject2.RemoveAt(0);
}
public void DeleteObject3()
{
if (ListTypesObject3 != null && ListTypesObject3.Count > 0)
ListTypesObject3.RemoveAt(0);
}
public void DeleteObject3List()
{
if (ListTypesObject3 == null)
return;
ListTypesObject3.Clear();
ListTypesObject3 = null;
AggregatedCollection.UnassignCollectionObject3();
}
public void CreateObject3List()
{
if (ListTypesObject3 != null)
return;
ListTypesObject3 = new ObservableCollection<Object3>()
{
new Object3("Added object 3 from new list 3"),
};
AggregatedCollection.AssignCollectionObject3(ListTypesObject3);
}
}
Full working example in this branch of the repository :
https://github.com/Whiletru3/pocbindingdmo/tree/ObservableCollectionAggregate
Im using Xamarin Forms Maps and I implemented location button:
Map.IsShowingUser = true;
Also I added Permissions (Xamarin Essentials nuget) and implemented like this in my view model:
public class MapViewModel : BaseViewModel
{
public MapViewModel(CustomMap map)
{
Map = map;
GetPermissions();
Task.Run(async () => { await GetImage(); });
}
public void SetPins()
{
if (App.FilterPins != null && App.FilterPins.Count > 0)
{
Device.BeginInvokeOnMainThread(() =>
{
Map.CustomPins.Clear();
Map.CustomPins.AddRange(App.FilterPins);
foreach (var item in App.FilterPins)
{
Map.Pins.Add(item);
App.SelectedPin = item;
item.InfoWindowClicked += Pin_InfoWindowClicked;
}
Map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(App.SelectedPin.Position.Latitude, App.SelectedPin.Position.Longitude), Distance.FromMiles(1.0)));
OnPropertyChanged(nameof(Map.CustomPins));
OnPropertyChanged(nameof(Map));
});
}
else
{
GetMapDefaultData();
}
}
public void SetMapToSelectedPin()
{
Device.BeginInvokeOnMainThread(() =>
{
if (App.SelectedPin != null)
{
//gledam da li je u listi pinova App.FilterPins ili u DefaultPins (iz default servisa)
if (Map.CustomPins.Any(x => x.Id == App.SelectedPin.Id))
{
//focusOnPin
Map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(App.SelectedPin.Position.Latitude, App.SelectedPin.Position.Longitude), Distance.FromMiles(1.0)));
}
else
{
//add pin to list -> Map.Pins.Add(pin);
//focusonpin
if (App.FilterPins != null && App.FilterPins.Count > 0)
{
Map.CustomPins.Add(App.SelectedPin);
Map.Pins.Add(App.SelectedPin);
Map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(App.SelectedPin.Position.Latitude, App.SelectedPin.Position.Longitude), Distance.FromMiles(1.0)));
}
}
}
else
{
Map.MoveToRegion(MapSpan.FromCenterAndRadius(Constants.BelgradeCoordinates, Distance.FromMiles(1.0)));
}
OnPropertyChanged(nameof(Map));
});
}
private async Task GetImage()
{
App.PinImages = new List<PinImageResponseModel>();
var result = await ApiServiceProvider.GetPinImages();
Device.BeginInvokeOnMainThread(() =>
{
App.PinImages = result;
});
}
public CustomPin _pin { get; set; }
public CustomPin Pin
{
get { return _pin; }
set { _pin = value; OnPropertyChanged(); }
}
public ICommand PositionMapOnPinCommand { get; set; }
private async void GetMapDefaultData()
{
try
{
DefaultRegionsAndClassesResponseModel regionsAndPinsResponse = new DefaultRegionsAndClassesResponseModel();
#region Regions
var regions = await ApiServiceProvider.GetRegionsQuery(null, 0, 5000);
//if (regions != null && regions.Data != null)
//{
// var polygon = new Polygon();
// foreach (var region in regions.Data.Where(x => x.Checked).ToList())
// {
// polygon.StrokeWidth = 8;
// polygon.StrokeColor = Color.Green;
// polygon.FillColor = Color.FromRgba(255, 0, 0, 64);
// foreach (var point in region.Points)
// {
// polygon.Geopath.Add(new Position(point.Latitude, point.Longitude));
// }
// Map.MapElements.Add(polygon);
// }
//}
#endregion
#region Pins & Infoboxes
regionsAndPinsResponse = await ApiServiceProvider.GetDefaultRegionsAndClasses();
Device.BeginInvokeOnMainThread(() =>
{
if (regionsAndPinsResponse.Succeeded && regionsAndPinsResponse.Data != null && regionsAndPinsResponse.Data.Count > 0)
{
foreach (var mainItem in regionsAndPinsResponse.Data)
{
CustomPin pin = new CustomPin();
if (mainItem != null && mainItem.Points.Count > 0)
{
pin.Id = mainItem.IdObjectInstance;
pin.ClassId = mainItem.ObjectClassIdObjectClass;
pin.Label = mainItem.ObjectClassName;
pin.IsFavorite = mainItem.IsFavorite.HasValue;
foreach (var item in App.PinImages)
{
if (mainItem.Status != null && mainItem.Status.ClassStatus.ClassStatusIcon != null && item.Name.Equals(mainItem.Status.ClassStatus.ClassStatusIcon))
{
string base64 = item.Base64String.Substring(item.Base64String.IndexOf(',') + 1);
base64 = base64.Trim('\0');
pin.InfoBox.ImageString = base64;
if (pin.InfoBox.ImageString != null)
{
var source = Convert.FromBase64String(pin.InfoBox.ImageString);
pin.InfoBox.PinImageSource = ImageSource.FromStream(() => new MemoryStream(source));
}
}
}
if (mainItem.Points != null)
{
pin.Position = new Position(mainItem.Points.FirstOrDefault().Latitude, mainItem.Points.FirstOrDefault().Longitude);
}
else
{
//add polygon
}
foreach (var item in mainItem.Strings)
{
pin.InfoBox.DetailsObjectInfos.Add(new Models.MapModels.DetailsObjectInfo { BoldLabelTitle = item.ClassParameterName + ": ", LabelValue = item.StringValue });
}
foreach (var item in mainItem.Integers)
{
pin.InfoBox.DetailsObjectInfos.Add(new Models.MapModels.DetailsObjectInfo { BoldLabelTitle = item.ClassParameterName + ": ", LabelValue = item.IntValue.ToString() });
}
}
pin.InfoWindowClicked += Pin_InfoWindowClicked;
Map.CustomPins.Add(pin);
Map.Pins.Add(pin);
}
}
});
#endregion
}
catch (Exception ex)
{
await Shell.Current.DisplayAlert("Error", "Something went wrong with maps", "OK");
}
}
private void Pin_InfoWindowClicked(object sender, PinClickedEventArgs e)
{
Device.BeginInvokeOnMainThread(async () =>
{
var pin = sender as CustomPin;
App.SelectedPin = pin;
await Shell.Current.Navigation.PushAsync(new ObjectParametersPage());
});
}
private async void GetPermissions()
{
var status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>();
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
}
if (status == PermissionStatus.Granted)
{
Map.IsShowingUser = true;
}
else
{
await Shell.Current.DisplayAlert("Permission Denied", "We Need to access your Location. But it is not granted", "OK");
}
}
public CustomMap Map { get; set; }
public ICommand SetCameraToChosenPinCommand { get; set; }
}
My XAML:
</ContentPage>
<ContentPage.Content>
<renderers:CustomMap x:Name="map" IsShowingUser="True"/>
</ContentPage.Content>
</ContentPage>
CodeBehind:
public partial class MapPage : ContentPage
{
MapViewModel mapViewModel;
public MapPage()
{
InitializeComponent();
BindingContext = mapViewModel = new MapViewModel(map);
}
protected override void OnAppearing()
{
base.OnAppearing();
mapViewModel.SetPins();
mapViewModel.SetMapToSelectedPin();
}
}
my CustomMap:
public class CustomMap : Map
{
//public List<CustomPin> CustomPins { get; set; }
public CustomMap()
{
CustomPins = new ObservableCollection<CustomPin>();
CustomPins.CollectionChanged += PinsSourceOnCollectionChanged;
}
public ObservableCollection<CustomPin> CustomPins
{
get { return (ObservableCollection<CustomPin>)GetValue(PinsSourceProperty); }
set { SetValue(PinsSourceProperty, value); OnPropertyChanged(nameof(CustomPins)); }
}
public static readonly BindableProperty PinsSourceProperty = BindableProperty.Create(
propertyName: "PinsSource",
returnType: typeof(ObservableCollection<CustomPin>),
declaringType: typeof(CustomMap),
defaultValue: null,
defaultBindingMode: BindingMode.TwoWay,
validateValue: null,
propertyChanged: PinsSourcePropertyChanged);
public MapSpan MapSpan
{
get { return (MapSpan)GetValue(MapSpanProperty); }
set { SetValue(MapSpanProperty, value); }
}
public static readonly BindableProperty MapSpanProperty = BindableProperty.Create(
propertyName: "MapSpan",
returnType: typeof(MapSpan),
declaringType: typeof(CustomMap),
defaultValue: null,
defaultBindingMode: BindingMode.TwoWay,
validateValue: null,
propertyChanged: MapSpanPropertyChanged);
private static void MapSpanPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var thisInstance = bindable as CustomMap;
var newMapSpan = newValue as MapSpan;
thisInstance?.MoveToRegion(newMapSpan);
}
private static void PinsSourcePropertyChanged(BindableObject bindable, object oldvalue, object newValue)
{
var thisInstance = bindable as CustomMap;
var newPinsSource = newValue as ObservableCollection<CustomPin>;
if (thisInstance == null ||
newPinsSource == null)
return;
UpdatePinsSource(thisInstance, newPinsSource);
}
private void PinsSourceOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
UpdatePinsSource(this, sender as IEnumerable<CustomPin>);
}
private static void UpdatePinsSource(CustomMap bindableMap, IEnumerable<CustomPin> newSource)
{
bindableMap.IsShowingUser = true;
bindableMap.Pins.Clear();
foreach (var pin in newSource)
bindableMap.Pins.Add(pin);
}
}
So, I have a bug only on first time installing app on my phone - it doesnt show location button, because its async method.
Each next tiem when I open the Map page there is a button.
So, my question would be: how to properly integrate permissions and map, in order to present location button on the first startup of application?
I am trying to Generically set the Field Values in the ItemsRow class (and others that are similar in format - but properties and fields are different.
I want to do something like what I do with hard coding
public Dictionary<object, Action<T, object>> SetFieldValues = new Dictionary<object, Action<T, object>>
{
{"Description", (m,v) => m.Description = (string)v};
}
public class Mapping<T> where T:Row
{
public Dictionary<object, Action<T, object>> SetFieldValues(string[] headers)
{
Dictionary<object, Action<T, object>> myDict = new Dictionary<object, Action<T, object>>();
// Activator
var objectType = typeof(T); // Type.GetType(T);
var tRow = Activator.CreateInstance(objectType) as Row;
foreach (var item in headers)
{
var myfield = tRow.FindFieldByPropertyName(item);
//myDict[item] = myfield =
//myDict[item] = (m, v) => m.FindFieldByPropertyName(item) = (Type.GetType(myfield.Type.ToString()))v;
}
// I want this to be dynamic header[i],(m,v) => m.(property to set for object) = (cast to type m.property type)v
//{"ItemName", (m,v) => m.ItemName = (string)v},
//{"Description", (m,v) => m.Description = (string)v},
return myDict;
}
// Class names will be different, properties and fields will be different..
public sealed class ItemsRow : Row
{
public String ItemName
{
get { return Fields.ItemName[this]; }
set { Fields.ItemName[this] = value; }
}
public String Description
{
get { return Fields.Description[this]; }
set { Fields.Description[this] = value; }
}
public static readonly RowFields Fields = new RowFields().Init();
public ItemsRow()
: base(Fields)
{
}
public class RowFields : RowFieldsBase
{
public StringField ItemName;
public StringField Description;
}
}
Base Class
=================================================================================
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
namespace Serenity.Data
{
[JsonConverter(typeof(JsonRowConverter))]
public abstract partial class Row : IEntityWithJoins,
INotifyPropertyChanged, IEditableObject
#if !COREFX
, IDataErrorInfo
#endif
{
internal RowFieldsBase fields;
internal bool[] assignedFields;
internal Hashtable dictionaryData;
internal bool ignoreConstraints;
internal object[] indexedData;
internal bool tracking;
internal bool trackWithChecks;
protected Row(RowFieldsBase fields)
{
if (fields == null)
throw new ArgumentNullException("fields");
this.fields = fields.InitInstance(this);
TrackAssignments = true;
}
public void CloneInto(Row clone,
bool cloneHandlers)
{
clone.ignoreConstraints = ignoreConstraints;
foreach (var field in GetFields())
field.Copy(this, clone);
clone.tracking = tracking;
if (tracking)
{
if (assignedFields != null)
{
clone.assignedFields = new bool[assignedFields.Length];
Array.Copy(assignedFields, clone.assignedFields, assignedFields.Length);
}
}
else
clone.assignedFields = null;
clone.trackWithChecks = trackWithChecks;
clone.originalValues = originalValues;
if (dictionaryData != null)
clone.dictionaryData = (Hashtable)this.dictionaryData.Clone();
else
clone.dictionaryData = null;
if (indexedData != null)
{
clone.indexedData = new object[indexedData.Length];
for (var i = 0; i < indexedData.Length; i++)
clone.indexedData[i] = indexedData[i];
}
else
clone.indexedData = null;
if (previousValues != null)
clone.previousValues = previousValues.CloneRow();
else
clone.previousValues = null;
if (cloneHandlers)
{
clone.postHandler = this.postHandler;
clone.propertyChanged = this.propertyChanged;
if (this.validationErrors != null)
clone.validationErrors = new Dictionary<string, string>(this.validationErrors);
else
clone.validationErrors = null;
}
}
public Row CloneRow()
{
var clone = CreateNew();
CloneInto(clone, true);
return clone;
}
public virtual Row CreateNew()
{
if (fields.rowFactory == null)
throw new NotImplementedException();
return fields.rowFactory();
}
internal void FieldAssignedValue(Field field)
{
if (assignedFields == null)
assignedFields = new bool[fields.Count];
assignedFields[field.index] = true;
if (validationErrors != null)
RemoveValidationError(field.PropertyName ?? field.Name);
if (propertyChanged != null)
{
if (field.IndexCompare(previousValues, this) != 0)
{
RaisePropertyChanged(field);
field.Copy(this, previousValues);
}
}
}
public Field FindField(string fieldName)
{
return fields.FindField(fieldName);
}
public Field FindFieldByPropertyName(string propertyName)
{
return fields.FindFieldByPropertyName(propertyName);
}
public RowFieldsBase GetFields()
{
return fields;
}
public int FieldCount
{
get { return fields.Count; }
}
public bool IsAnyFieldAssigned
{
get
{
return tracking && assignedFields != null;
}
}
public bool IgnoreConstraints
{
get { return ignoreConstraints; }
set { ignoreConstraints = value; }
}
public string Table
{
get { return fields.TableName; }
}
public bool TrackAssignments
{
get
{
return tracking;
}
set
{
if (tracking != value)
{
if (value)
{
if (propertyChanged != null)
previousValues = this.CloneRow();
tracking = value;
}
else
{
tracking = false;
trackWithChecks = false;
assignedFields = null;
}
}
}
}
public bool TrackWithChecks
{
get
{
return tracking && trackWithChecks;
}
set
{
if (value != TrackWithChecks)
{
if (value && !tracking)
TrackAssignments = true;
trackWithChecks = value;
}
}
}
private Field FindFieldEnsure(string fieldName)
{
var field = FindField(fieldName);
if (ReferenceEquals(null, field))
throw new ArgumentOutOfRangeException("fieldName", String.Format(
"{0} has no field with name '{1}'.", this.GetType().Name, fieldName));
return field;
}
public object this[string fieldName]
{
get
{
var field = FindFieldByPropertyName(fieldName) ??
FindField(fieldName);
if (ReferenceEquals(null, field))
{
if (dictionaryData != null)
return dictionaryData[fieldName];
return null;
}
return field.AsObject(this);
}
set
{
(FindFieldByPropertyName(fieldName) ??
FindFieldEnsure(fieldName)).AsObject(this, value);
}
}
public void SetDictionaryData(object key, object value)
{
if (value == null)
{
if (dictionaryData == null)
return;
dictionaryData[key] = null;
}
else
{
if (dictionaryData == null)
dictionaryData = new Hashtable();
dictionaryData[key] = value;
}
}
public object GetDictionaryData(object key)
{
if (dictionaryData != null)
return dictionaryData[key];
return null;
}
internal void SetIndexedData(int index, object value)
{
if (value == null)
{
if (indexedData == null)
return;
indexedData[index] = null;
}
else
{
if (indexedData == null)
indexedData = new object[this.FieldCount];
indexedData[index] = value;
}
}
internal object GetIndexedData(int index)
{
if (indexedData != null)
return indexedData[index];
return null;
}
public bool IsAssigned(Field field)
{
if (assignedFields == null)
return false;
return assignedFields[field.index];
}
public void ClearAssignment(Field field)
{
if (assignedFields == null)
return;
assignedFields[field.index] = false;
for (var i = 0; i < assignedFields.Length; i++)
if (assignedFields[i])
return;
assignedFields = null;
}
public bool IsAnyFieldChanged
{
get
{
if (originalValues == null)
return false;
for (var i = 0; i < fields.Count; i++)
if (fields[i].IndexCompare(originalValues, this) != 0)
return true;
return false;
}
}
IDictionary<string, Join> IHaveJoins.Joins
{
get { return fields.Joins; }
}
}
}
Sorry I couldn't get back to you yesterday, but a promise is a promise :). And while writing this code I began to doubt about what your headers actually look like. How are you going to determine which property belongs to which header? Unless you have some type of recognition for that which I might be missing from your code.
But the general idea is this:
Of course this is based on the premise that headers are keyvaluepairs of which the key is the identifier for which property to use.
static void Main(string[] args)
{
var values = new Dictionary<string, string>()
{
["Title"] = "Test",
["Amount"] = "5",
["Description"] = "Some description"
};
var target = new TestClass();
var setters = GetPropertySetters(target);
foreach(KeyValuePair<string, string> value in values)
{
if (setters.ContainsKey(value.Key))
setters[value.Key].Invoke(value.Value);
}
Console.WriteLine(JsonConvert.SerializeObject(target));
Console.ReadLine();
}
public static Dictionary<string, Action<string>> GetPropertySetters<T>(T source)
{
var result = new Dictionary<string, Action<string>>(StringComparer.OrdinalIgnoreCase);
foreach (PropertyInfo pi in typeof(T).GetProperties())
result.Add(pi.Name, (string value) => { pi.SetValue(source, Convert.ChangeType(value, pi.PropertyType)); });
return result;
}
public class TestClass
{
public string Title { get; set; }
public int Amount { get; set; }
public string Description { get; set; }
}
Like the title says, I try to notify a property change, the method RaisePropertyChanged is called coorectly, but PropertyChanged is always null.
Here the shortened class:
public class BluetoothManager : INotifyPropertyChanged {
private string selectedBluetoothResult;
private List<string> foundDevices = new List<string>(5);
public List<DeviceInformation> penDevices = new List<DeviceInformation>();
private GattCharacteristic TxCharacteristic;
public string SelectedBluetoothResult {
get {
return selectedBluetoothResult;
}
set {
selectedBluetoothResult = value;
RaisePropertyChanged();
}
}
public List<string> FoundDevices {
get
{
return foundDevices;
}
set
{
foundDevices = value;
RaisePropertyChanged();
}
}
public BluetoothManager() {
StartScanWatcher();
}
public void StartScanWatcher() {
Debug.WriteLine("Starting device watcher...");
String query = "";
//query for Bluetooth LE devices
query += "System.Devices.DevObjectType:=5 AND System.Devices.Aep.ProtocolId:=\"{BB7BB05E-5972-42B5-94FC-76EAA7084D49}\"";
//query for devices with controllers' name
query += " AND (System.ItemNameDisplay:=\"" + DeviceName + "\" )";
var deviceWatcher = DeviceInformation.CreateWatcher(query); //, requestedProperties, DeviceInformationKind.AssociationEndpoint);
deviceWatcher.Added += DeviceWatcher_OnAdded;
deviceWatcher.EnumerationCompleted += DeviceWatcher_OnEnumComplete;
deviceWatcher.Removed += DeviceWatcher_Removed;
deviceWatcher.Stopped += DeviceWatcher_Stopped;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Start();
Debug.WriteLine(" StartScanWatcher end");
}
private void DeviceWatcher_OnAdded(DeviceWatcher sender, DeviceInformation deviceInfo) {
Debug.WriteLine(" DeviceWatcher_OnAdded Start");
lock (foundDevices) {
if (foundDevices.Contains(deviceInfo.Id)) {
return;
}
foundDevices.Add(deviceInfo.Id);
RaisePropertyChanged("FoundDevices");
}
Debug.WriteLine($"[{deviceInfo.Name}] DeviceWatcher_OnAdded...");
if (SelectedBluetoothResult == null)
{
SelectedBluetoothResult = deviceInfo.Id;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
Debug.WriteLine("<<<<<<<<<<<<< BluetoothManager, PropertyChanging: " + propertyName);
if (PropertyChanged == null) {
Debug.WriteLine("============ BluetoothManager, PropertyChanged == null, " + propertyName);
return;
}
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
Debug.WriteLine(">>>>>>>>>>>>> BluetoothManager, PropertyChanged: " + propertyName);
}
}
}
And here the Binding in the XAML file:
<ListView ItemsSource="{Binding BluetoothManager.FoundDevices}" SelectedItem="{Binding BluetoothManager.SelectedBluetoothResult}">
<ListView.Resources>
<DataTemplate x:Key="BluetoothDeviceTemplate">
<TextBlock Text="{Binding Path=Sap}"/>
</DataTemplate>
</ListView.Resources>
</ListView>
The binding seems to work properly.
Please note, that the class in which the PropertyChanged is always null is not the DataContext auf the XAML file. Does that mean I have to work differently with the PropertyChange notification?
Thanks in advance.
EDIT:
The complete MainWindowViewModel:
public class MainWindowViewModel : INotifyPropertyChanged {
private ObservableCollection<BookGame> _booksToDisplay = new ObservableCollection<BookGame>();
private ObservableCollection<BookGame> _games = new ObservableCollection<BookGame>();
private string _url;
private int[] _newBooksMID;
private int[] _oldBooksMID;
private Dictionary<int, int> _newBooksVersionNumbers;
private Dictionary<int, int> _oldBooksVersionNumbers;
private Dictionary<int, int> _allVersionNumbers;
private List<BookGame> _booksToAdd;
private long _downloadSpeed;
private bool _isDownloading = true;
private bool _toggleLastSearchKeyWasReturn = false;
string _volumeLabel = "";
FileInfo[] _filesTxt = { };
FileInfo[] _filesAll = { };
private string _folderPath = "";
private string _driveName = null;
List<BookGame> _allGames;
List<BookGame> _allBooks;
List<MP3> _allMP3;
long lengthAllBooks = 0;
long lengthAllGames = 0;
int _percentDownloaded = 100;
private long _amountBytesToDownload;
private long _amountBytesDownloaded;
private long _amountMBytesToDownload;
private long _amountMBytesDownloaded;
private int _downloadTime;
//private bool _isDownloadAborted;
ServerCommi serverComm;
public BluetoothManager BluetoothManager { get; set; }
public MainWindowViewModel() {
DownloadTime = 0;
AmountBytesToDownload = 0;
AmountBytesDownloaded = 0;
DriveInfo drive = null;
foreach (DriveInfo driveInfo in DriveInfo.GetDrives()) {
if (driveInfo.IsReady && driveInfo.VolumeLabel == _volumeLabel) {
drive = driveInfo;
_driveName = drive.Name;
_folderPath = _driveName + _folderPath;
}
}
DirectoryInfo di = new DirectoryInfo(_folderPath);
if (di.Exists)
{
_filesTxt = di.GetFiles("*.txt");
FilesAll = di.GetFiles("*.*");
foreach (FileInfo file in _filesTxt)
{
try
{
Convert.ToInt32(file.Name.Split('_')[0]);
AddBookGameToList(file);
}
catch (Exception e)
{
}
}
}
SearchResults = new ObservableCollection<ResultItem>();
MenuCommand = new RelayCommand(o => {
Debug.WriteLine("Menu Command " + o);
SwitchBooks(o);
});
SearchReturnKeyCommand = new RelayCommand(o => {
Debug.WriteLine("00000000000000000000000000000000 SearchReturnKeyCommand " + o);
SearchActivated();
});
BrowserCommand = new RelayCommand(o => {
Debug.WriteLine("Browser Command main" + o);
CallBrowser("");
});
DeleteCommand = new RelayCommand(o => {
Debug.WriteLine("Delete Command main" + o);
});
ToggleDownloadsCommand = new RelayCommand(o => {
Debug.WriteLine(" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ");
Debug.WriteLine(" |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ");
Debug.WriteLine("ToggleDownloadsCommand Command main" + o);
ToggleDownloads();
});
_viewModelMusic = new ViewModelMusic(_driveName);
AllGames = Games.ToList<BookGame>();
AllBooks = BooksToDisplay.ToList<BookGame>();
AllMP3 = _viewModelMusic.Mp3s.ToList<MP3>();
long lengthAllMP3 = 0;
foreach (MP3 mp3 in AllMP3) {
lengthAllMP3 += mp3.LengthValue;
}
_viewModelBooks = new ViewModelBooks(BooksToDisplay);
_viewModelGames = new ViewModelGames(Games);
_viewModelFiles = new ViewModelFiles(FilesAll);
_viewModelLumi = new ViewModelLumi(drive, lengthAllBooks, lengthAllGames, lengthAllMP3);
_viewModelOverview = new ViewModelOverview(AllBooks, AllGames, AllMP3);
_screens[0] = _viewModelOverview;
_screens[1] = _viewModelBooks;
_screens[2] = _viewModelGames;
_screens[3] = _viewModelMusic;
_screens[4] = _viewModelFiles;
_screens[5] = _viewModelVideos;
_screens[6] = _viewModelAdults;
_screens[7] = _viewModelLumi;
SearchText = "";
SelectedItem = _viewModelBooks;
Debug.WriteLine("CALLING ServerCommi! Ring, ring!");
serverComm = new ServerCommi(this);
//serverComm.DownloadBooksAsync();
BluetoothManager = new BluetoothManager();
}
private void ToggleDownloads() {
IsDownloading = !IsDownloading;
serverComm.ToggleDownloads(IsDownloading);
_viewModelBooks.ToggleDownloads(IsDownloading);
}
internal void DownloadStateChange(int mid, int newState) {
_viewModelBooks.DownloadStateChange(mid, newState);
}
// params bool[] isDownload : varargs
// returns the mid
public int AddBookGameToList(FileInfo file, bool isDownload = false) {
BookGame bg = new BookGame(file);
if (isDownload) {
bg.DownloadState = 2;
if (bg.Mid == serverComm.DownloadingMID) {
bg.DownloadState = 1;
}
}
if (bg.Group.StartsWith("B")) {
bg.Group = "Bücher";
}
switch (bg.Group) {
case "Bücher":
if (isDownload) {
BooksToDisplay.Insert(0, bg);
} else {
BooksToDisplay.Add(bg);
}
lengthAllBooks += bg.LengthValue;
break;
case "Spiele":
Games.Add(bg);
lengthAllGames += bg.LengthValue;
break;
default:
Debug.WriteLine("Default: " + bg.Title);
break;
}
return bg.Mid;
}
private void CallBrowser(string url) {
Debug.WriteLine("Url: " + Url);
try {
System.Diagnostics.Process.Start(Url);
} catch (System.ComponentModel.Win32Exception noBrowser) {
if (noBrowser.ErrorCode == -2147467259)
MessageBox.Show(noBrowser.Message);
} catch (System.Exception other) {
MessageBox.Show(other.Message);
}
}
string _searchText;
public string SearchText {
get {
return _searchText;
}
set {
Debug.WriteLine("SearchText Value= " + value);
if (!_toggleLastSearchKeyWasReturn) {
_searchText = value;
SearchResults.Clear();
List<ResultItem> _allBooksRI = new List<ResultItem>();
List<ResultItem> _allBooksHelperList = _allBooks.ToList<ResultItem>();
List<ResultItem> _allGamesHelperList = _allGames.ToList<ResultItem>();
List<ResultItem> _allMP3HelperList = _allMP3.ToList<ResultItem>();
if (SelectedItem != null && SelectedItem.Equals(_viewModelGames)) {
AddAllResultItemsIf(SearchResults, _allGamesHelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allBooksHelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allMP3HelperList, _searchText);
Debug.WriteLine("===================================== Games - " + SearchResults);
Debug.WriteLine("SelectedItem - " + SelectedItem);
} else if (SelectedItem != null && SelectedItem.Equals(_viewModelMusic)) {
AddAllResultItemsIf(SearchResults, _allMP3HelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allGamesHelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allBooksHelperList, _searchText);
Debug.WriteLine("====================================== Music " + SearchResults);
Debug.WriteLine("SelectedItem - " + SelectedItem);
} else {
AddAllResultItemsIf(SearchResults, _allBooksHelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allGamesHelperList, _searchText);
AddAllResultItemsIf(SearchResults, _allMP3HelperList, _searchText);
Debug.WriteLine("====================================== Books " + SearchResults);
}
if (SearchResults.Count == 0) {
SearchResults.Add(new ErrorResultItem("Error", "Nichts passendes gefunden."));
}
} else {
_toggleLastSearchKeyWasReturn = false;
}
}
}
private ObservableCollection<ResultItem> AddAllResultItemsIf(ObservableCollection<ResultItem> searchResults, List<ResultItem> toAdd, string searchText) {
foreach (ResultItem item in toAdd) {
if (item.Title.ToLower().Contains(_searchText.ToLower())) {
searchResults.Add(item);
}
}
return searchResults;
}
public ObservableCollection<ResultItem> SearchResults {
get; set;
}
ResultItem _selectedResult;
public ResultItem SelectedResult {
get {
return _selectedResult;
}
set {
_selectedResult = value;
SearchItemSelected(value);
}
}
private void SearchItemSelected(ResultItem value) {
switch (value.Group) {
case "Bücher":
SelectedItem = _viewModelBooks;
break;
case "Spiele":
SelectedItem = _viewModelGames;
break;
case "Musik":
SelectedItem = _viewModelMusic;
break;
default:
Debug.WriteLine("Search Item Selected, jumped to default: " + value);
break;
}
Unmark(Marked);
Mark(value);
}
ResultItem _marked;
internal void Mark(ResultItem value) {
Marked = value;
value.Marked = true;
}
internal void Unmark(ResultItem value) {
Marked = null;
if (value != null) {
value.Marked = false;
}
}
public ResultItem Marked {
get => _marked;
set => _marked = value;
}
private bool _isSearchResult;
public bool IsSearchResult {
get {
return _isSearchResult;
}
set {
_isSearchResult = value;
Debug.WriteLine("IsSearchResult= " + value);
RaisePropertyChanged();
}
}
private void SearchActivated() {
_toggleLastSearchKeyWasReturn = true;
SelectedItem = _viewModelOverview;
IsSearchResult = true;
}
private object _selectedItem;
public object SelectedItem {
get {
return _selectedItem;
}
set {
_selectedItem = value;
Debug.WriteLine("SELECTED_ITEM SETTER: " + value);
Unmark(Marked);
IsSearchResult = false;
if (SearchText != null) {
SearchText = SearchText;
}
RaisePropertyChanged();
}
}
ViewModelOverview _viewModelOverview;
ViewModelBooks _viewModelBooks;
ViewModelGames _viewModelGames;
ViewModelMusic _viewModelMusic;
ViewModelFiles _viewModelFiles;
ViewModelVideos _viewModelVideos = new ViewModelVideos();
ViewModelAdults _viewModelAdults = new ViewModelAdults();
ViewModelLumi _viewModelLumi;
object[] _screens = new object[8];
public object[] Screens {
get {
return _screens;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string propertyName = null) {
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public ICommand MenuCommand {
get; set;
}
public ICommand SearchReturnKeyCommand {
get; set;
}
public ICommand BrowserCommand {
get; set;
}
public ICommand ToggleDownloadsCommand {
get; set;
}
public RelayCommand DeleteCommand {
get;
private set;
}
public List<BookGame> AllGames {
get => _allGames;
set => _allGames = value;
}
public List<BookGame> AllBooks {
get => _allBooks;
set => _allBooks = value;
}
public List<MP3> AllMP3 {
get => _allMP3;
set => _allMP3 = value;
}
public ViewModelBooks ViewModelBooks {
get => _viewModelBooks;
set => _viewModelBooks = value;
}
public ObservableCollection<BookGame> BooksToDisplay {
get => _booksToDisplay;
set => _booksToDisplay = value;
}
public ObservableCollection<BookGame> Games {
get => _games;
set => _games = value;
}
public string Url {
get {
return _url;
}
set {
_url = value;
RaisePropertyChanged();
}
}
public int[] NewBooksMID {
get => _newBooksMID;
set => _newBooksMID = value;
}
public int[] OldBooksMID {
get => _oldBooksMID;
set => _oldBooksMID = value;
}
public Dictionary<int, int> NewBooksVersionNumbers {
get => _newBooksVersionNumbers;
set => _newBooksVersionNumbers = value;
}
public Dictionary<int, int> OldBooksVersionNumbers {
get => _oldBooksVersionNumbers;
set => _oldBooksVersionNumbers = value;
}
public Dictionary<int, int> AllVersionNumbers {
get => _allVersionNumbers;
set => _allVersionNumbers = value;
}
public int[] OldBooksMID1 {
get => _oldBooksMID;
set => _oldBooksMID = value;
}
public List<BookGame> BooksToAdd {
get => _booksToAdd;
set => _booksToAdd = value;
}
public FileInfo[] FilesAll {
get => _filesAll;
set => _filesAll = value;
}
public string FolderPath {
get => _folderPath;
set => _folderPath = value;
}
public int PercentDownloaded {
get {
return _percentDownloaded;
}
set {
_percentDownloaded = value;
RaisePropertyChanged();
}
}
public long DownloadSpeed {
get {
return _downloadSpeed;
}
set {
_downloadSpeed = value;
RaisePropertyChanged();
}
}
public long AmountBytesToDownload {
get {
return _amountBytesToDownload;
}
set {
_amountBytesToDownload = value;
Debug.WriteLine("Property Changed: " + "AmountBytesToDownload");
AmountMBytesToDownload = value / 1024 / 1024;
}
}
public long AmountBytesDownloaded {
get {
return _amountBytesDownloaded;
}
set {
_amountBytesDownloaded = value;
AmountMBytesDownloaded = value / 1024 / 1024;
}
}
public int DownloadTime {
get {
return _downloadTime;
}
set {
_downloadTime = value;
RaisePropertyChanged();
}
}
/*
public bool IsDownloadAborted {
get {
return _isDownloadAborted;
}
set {
_isDownloadAborted = value;
RaisePropertyChanged();
}
}
*/
public long AmountMBytesDownloaded {
get {
return _amountMBytesDownloaded;
}
set {
_amountMBytesDownloaded = value;
RaisePropertyChanged();
}
}
public long AmountMBytesToDownload {
get {
return _amountMBytesToDownload;
}
set {
_amountMBytesToDownload = value;
RaisePropertyChanged();
}
}
public bool IsDownloading {
get {
return _isDownloading;
}
set {
_isDownloading = value;
RaisePropertyChanged();
}
}
internal void SwitchBooks(object o) {
Debug.WriteLine("SwitchBooksEx " + o);
if (o.ToString().Equals("Tessloff.ViewModelBooks")) {
((ViewModelBooks)_screens[0]).SwitchView();
Debug.WriteLine("SwitchBooksIn " + o);
}
}
}
public class CommandViewModel {
private MainWindowViewModel _viewmodel;
public CommandViewModel(MainWindowViewModel viewmodel) {
_viewmodel = viewmodel;
Debug.WriteLine("LALALALALA");
MenuCommand = new RelayCommand(o => {
Debug.WriteLine("CommandViewModel " + o);
_viewmodel.SwitchBooks(o);
});
DeleteCommand = new RelayCommand(o => {
Debug.WriteLine("Delte Command CVM" + o);
});
}
public ICommand MenuCommand {
get; set;
}
public ICommand DeleteCommand {
get; set;
}
public string Title {
get;
private set;
}
}
public class RelayCommand : ICommand {
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null) {
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute) {
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter) {
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged {
add {
CommandManager.RequerySuggested += value;
}
remove {
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter) {
_execute(parameter);
}
#endregion // ICommand Members
}
public class ViewModelAdults {
public ViewModelAdults() {
Title = "Erwachsene";
ImgUrl = "/Resources/Erwachsene.png";
}
public string Title {
get; set;
}
public string ImgUrl {
get;
private set;
}
}
Edit tl;dr:
Why do all "direct" properties of MainWindowViewModel update great (like MainWindowViewModel.IsSearchResult), but the two "indirect" properties don't (MainWindowViewModel.BluetoothManager.SelectedBluetoothResult).
List.add() doesnt trigger PropertyChange. You need to use ObservableCollection or raise PropertyChange yourself after adding item.
I've a WPF application that loads a menu from an XML file, each node as a tag that identifies the user function. Each user has visibility permission that match against the tag defined in the xml file. I wish some help on simplifing that code since I's quite complex and from my point of view poor performing. Consider that the main menu is composed of main items and inside each there're specific areas function. If a user is enabled to at element at list the main menu node is shown otherwise not.
public virtual System.Threading.Tasks.Task<MenuItemNode> RegisterMenu(IDictionary<string,Type> functions)
{
var assembly = Assembly.GetCallingAssembly(); //I should get the module that invoked the base class
string filename = GetFullFileName(assembly, MenuFilename);
return Task.Factory.StartNew<MenuItemNode>(() =>
{
string xmlFileName = string.Format(filename);
var doc = new XmlDocument();
using (Stream stream = assembly.GetManifestResourceStream(xmlFileName))
{
if (stream != null)
{
using (var reader = new StreamReader(stream))
{
doc.LoadXml(reader.ReadToEnd());
}
}
}
MenuItemNode menu = BuildMenu(doc.SelectSingleNode(#"/Node"), "/", functions);
return menu;
});
}
private string GetFullFileName(Assembly assembly,string filename)
{
var resourceFiles = assembly.GetManifestResourceNames();
return resourceFiles.First(x => x.EndsWith(filename));
}
private MenuItemNode BuildMenu(XmlNode parent, string path, IDictionary<string, Type> functions)
{
Argument.IsNotNull(() => parent);
if (functions == null || (functions.Count == 0)) return null;
MenuItemNode menuItem = null;
string subPath = "Node";
string name = string.Empty;
string tag = string.Empty;
int position = 0;
bool forceVisible = false;
string parameters = string.Empty;
string group = string.Empty;
bool showInDialog = false;
if (parent.Attributes != null)
{
if (parent.Attributes["name"] != null)
name = parent.Attributes["name"].Value;
if (parent.Attributes["tag"] != null)
tag = parent.Attributes["tag"].Value;
if (parent.Attributes["position"] != null)
position = System.Convert.ToInt32(parent.Attributes["position"].Value);
if (parent.Attributes["force_visible"] != null)
forceVisible = Convert.ToBoolean(parent.Attributes["force_visible"].Value);
if (parent.Attributes["parameters"] != null)
parameters = parent.Attributes["parameters"].Value;
if (parent.Attributes["group"] != null)
group = parent.Attributes["group"].Value;
if (parent.Attributes["showindialog"] != null)
showInDialog = Convert.ToBoolean(parent.Attributes["showindialog"].Value);
}
//parent item
if (string.IsNullOrEmpty(tag))
{
menuItem = CreateMenuItem(name, position);
menuItem.ForceVisible = forceVisible;
// menuItem.Group = group;
}
else//child item
{
if (functions.ContainsKey(tag))
{
menuItem = CreateMenuItem(name, tag, position);
menuItem.ForceVisible = forceVisible;
//menuItem.GroupName = group;
menuItem.ShowInDialog = showInDialog;
//menuItem.MenuParameter = GetMenuItemParameters(parameters);
#region Multiple-tag
if ((functions == null) || !functions.Any()) return null;
#endregion
}
else
{
//todo: add-logging
}
}
if (parent.HasChildNodes)
{
foreach (XmlNode child in parent.SelectNodes(subPath))
{
MenuItemNode childMenuItem = BuildMenu(child, subPath, functions);
if (childMenuItem == null) continue;
int childPosition = childMenuItem.SortIndex;
//This to prevent out-of-boundaries exception
if (childPosition > menuItem.Children.Count)
childPosition = menuItem.Children.Count;
menuItem.Children.Insert(childPosition, childMenuItem);
}
}
return menuItem;
}
private MenuItemNode CreateMenuItem(string text, int position)
{
var item = new MenuItemNode();
item.Text = text;
item.SortIndex = position;
return item;
}
private MenuItemNode CreateMenuItem(string text, string tag, int? position)
{
MenuItemNode item = CreateMenuItem(text, (!position.HasValue) ? 0 : position.Value);
item.FunctionTag = tag;
item.SortIndex = (!position.HasValue) ? 0 : position.Value;
return item;
}
And here's the MenuItemNode class
[ContentProperty("Children")]
public class MenuItemNode : INotifyPropertyChanged
{
private string text;
private ICommand command;
private Uri imageSource;
private int sortIndex;
private bool forceVisible;
private bool showInDialog;
private bool isChecked;
public bool IsChecked
{
get
{
return this.isChecked;
}
set
{
if (this.isChecked != value)
{
this.isChecked = value;
this.RaisePropertyChanged(() => this.IsChecked);
}
}
}
public bool IsSeparator { get; set; }
public MenuItemNode()
{
Children = new MenuItemNodeCollection();
SortIndex = 50;
SetCommand();
}
public MenuItemNode(String path)
: base()
{
Path = path;
}
public MenuItemNodeCollection Children { get; private set; }
public virtual ICommand Command
{
get
{
return command;
}
set
{
if (command != value)
{
command = value;
RaisePropertyChanged(() => this.Command);
}
}
}
public Uri ImageSource
{
get
{
return imageSource;
}
set
{
if (imageSource != value)
{
imageSource = value;
RaisePropertyChanged(() => this.ImageSource);
}
}
}
public string Text
{
get
{
return text;
}
set
{
if (text != value)
{
text = value;
RaisePropertyChanged(() => this.Text);
}
}
}
private MenuGroupDescription group;
public MenuGroupDescription Group
{
get { return group; }
set
{
if (group != value)
{
group = value;
RaisePropertyChanged(() => this.Group);
}
}
}
public int SortIndex
{
get
{
return sortIndex;
}
set
{
if (sortIndex != value)
{
sortIndex = value;
RaisePropertyChanged(() => this.SortIndex);
}
}
}
public string Path
{
get;
private set;
}
public bool ForceVisible
{
get
{
return this.forceVisible;
}
set
{
if (forceVisible != value)
{
this.forceVisible = value;
RaisePropertyChanged(() => ForceVisible);
}
}
}
public bool ShowInDialog
{
get
{
return this.showInDialog;
}
set
{
if (showInDialog = value)
{
this.showInDialog = value;
RaisePropertyChanged(() => ShowInDialog);
}
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyExpression.Name));
}
#endregion
protected virtual void SetCommand()
{
this.Command = FunctionMenuCommands.OpenFunctionCommand;
}
public string FunctionTag { get; set; }
}
In specific what I did is to process each child node then if visible I add it to the collection... do you see any possible better solution?
Thanks
Whoever wrote that code clearly didn't know how to write WPF. In WPF, there is a much simpler option... just use the MenuItem.Visibility property to hide (collapse) MenuItems that users don't have access to. Of course, you'd need some data bind-able security bool properties which you could then data bind to the MenuItem.Visibility property of each MenuItem via a BooleanToVisibilityConverter. Here's a simple example:
<MenuItem Header="Some option" Visibility="{Binding User.Security.HasSomeOptionPermission,
Converter={StaticResource BooleanToVisibilityConverter}}" ... />
If a particular user has the SomeOptionPermission, then the MenuItem will be displayed, otherwise it will be hidden.