I create WebChannelFactory. I need set KeepAlive=false.
I found only one solution use CustomBinding. and set property keepAliveEnabled to false.
I use custom behavior for my factory also.
Code:
static CustomBinding GetBinding(MySettings serviceSettings = null)
{
var customBinding = new CustomBinding();
HttpTransportBindingElement transportBindingElement = new HttpTransportBindingElement();
transportBindingElement.KeepAliveEnabled = false;
transportBindingElement.MaxBufferSize = 0x07000000;
transportBindingElement.MaxReceivedMessageSize = 0x07000000;
if (serviceSettings != null)
{
customBinding.SendTimeout = serviceSettings.SendTimeout;
}
customBinding.Elements.Add(transportBindingElement);
return customBinding;
}
var customBinding = GetBinding(serviceSettings);
WebChannelFactory<TChannel> factory = new WebChannelFactory<TChannel>(customBinding, new Uri(url));
factory.Endpoint.Behaviors.Add(new MyWebHttpBehavior(userId));
class MyWebHttpBehavior : IEndpointBehavior
{
private int _userId;
public MyWebHttpBehavior(int userId)
{
_userId = userId;
}
public void AddBindingParameters(ServiceEndpoint serviceEndpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{ }
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.ClientRuntime behavior)
{
behavior.MessageInspectors.Add(new MyClientMessageInspector(_userId));
}
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{ }
public void Validate(ServiceEndpoint serviceEndpoint)
{ }
}
In current situation i get error "does not have a Binding with the None MessageVersion".
Related
I'm making an editor in PropertyDrawer using SceneView.duringSceneGui. So it involves subscribing to SceneView.duringSceneGui when a property needs to draw stuff in SceneView and unsubscribing when it's gone. However I have no idea how to know if edited array element was removed from an array. It still exists in the memory and SceneView.duringSceneGui subscribed method is still there. I need to know when to stop editing and unsubscribe from it.
I guess I need to implement some context object, to store property value, edited object, PropertyDrawer and that subscription method should be there, to be able to unsubscribe exactly that editor... Although there may be only one editor running at once.
Does anybody found that out? Couldn't find anything with PropertyDrawers and array elements being deleted or removed.
TL.DR. Does Unity has an event to tell that PropertyDrawer's array element was removed or is there a simple or neat way to figure this out?
So, while making an example, I've solved my problem by getting property value every SceneView draw call and on catching an exception or if that value isn't edited stopped editor. I've added [NonSerialized] to _IsInEditMode to fix a new issue that I caught at the last moment, so that one is crucial.
Not sure if it's the best way to do that. If anybody will ever need to make a SceneView editor for some class, here's the example that works on arrays and lists also. Just separate it into 3 files and put them in respective folders, like Editor/ for MyClassDrawer.
using InspectorSerializedUtility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
public class MyScript : MonoBehaviour
{
public MyClass field;
public List<MyClass> list = new List<MyClass>();
}
[Serializable]
public class MyClass
{
#if UNITY_EDITOR
[NonSerialized]
public bool _IsInEditMode;
#endif
public Vector3 position;
public void Reset()
{
position = Vector3.zero;
#if UNITY_EDITOR
_IsInEditMode = false;
#endif
}
}
[CustomPropertyDrawer(typeof(MyClass))]
public class MyClassDrawer : PropertyDrawer
{
public MyClass value;
public Transform targetTransform;
private Tool internalTool;
bool editorStarted {
get => value?._IsInEditMode ?? false;
set {
if (this.value != null)
this.value._IsInEditMode = value;
}
}
private SerializedProperty currentProperty;
private SerializedProperty drawerProperty;
private static MyClassDrawer currentlyEditedDrawer;
string editorButtonText(bool isInEditMode) => isInEditMode ? "Stop Editing" : "Start Editing";
Color editorButtonColor(bool isInEditMode) => isInEditMode ? Color.red + Color.white / 2f : Color.white;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
//Debug.Log("OnGUI");
drawerProperty = property;
targetTransform = ((Component)property.serializedObject.targetObject).transform;
var val = property.GetValue<MyClass>();
GUI.color = editorButtonColor(val._IsInEditMode);
var toggle = GUI.Toggle(position, val._IsInEditMode, editorButtonText(val._IsInEditMode), "Button");
if (toggle != val._IsInEditMode)
{
if (toggle && currentlyEditedDrawer != null && currentlyEditedDrawer.editorStarted)
currentlyEditedDrawer.StopEditor();
value = val;
currentlyEditedDrawer = this;
if (toggle)
StartEditor();
else
StopEditor();
}
GUI.color = Color.white;
}
public void OnDrawScene(SceneView sv) => OnDrawScene();
public void OnDrawScene()
{
//Debug.Log("OnDrawScene");
MyClass value = null;
try
{
value = currentProperty.GetValue<MyClass>();
if (!value._IsInEditMode)
{
StopEditor();
return;
}
} catch
{
StopEditor();
return;
}
var m = Handles.matrix;
Handles.matrix = targetTransform.localToWorldMatrix;
if (Tools.current == Tool.Move)
{
internalTool = Tool.Move;
Tools.current = Tool.None;
}
if (internalTool == Tool.Move)
{
var pos = Handles.PositionHandle(value.position, Quaternion.identity);
if (value.position != pos)
{
Undo.RecordObject(targetTransform, "position changed");
value.position = pos;
}
}
Handles.matrix = m;
}
public void StartEditor()
{
currentProperty = drawerProperty;
editorStarted = true;
Debug.Log("StartEditor");
Subscribe();
CallAllSceneViewRepaint();
}
private void Subscribe()
{
Unsubscribe();
SceneView.duringSceneGui += OnDrawScene;
Selection.selectionChanged += StopEditor;
EditorSceneManager.sceneClosed += StopEditor;
AssemblyReloadEvents.beforeAssemblyReload += StopEditor;
}
public void StopEditor(Scene s) => StopEditor();
public void StopEditor()
{
Tools.current = internalTool;
editorStarted = false;
Unsubscribe();
currentProperty = null;
CallAllSceneViewRepaint();
}
private void Unsubscribe()
{
SceneView.duringSceneGui -= OnDrawScene;
Selection.selectionChanged -= StopEditor;
EditorSceneManager.sceneClosed -= StopEditor;
AssemblyReloadEvents.beforeAssemblyReload -= StopEditor;
}
private void CallAllSceneViewRepaint()
{
foreach (SceneView sv in SceneView.sceneViews)
sv.Repaint();
}
}
namespace InspectorSerializedUtility
{
/// <summary>
/// https://gist.github.com/douduck08/6d3e323b538a741466de00c30aa4b61f
/// </summary>
public static class InspectorSeriallizedUtils
{
public static T GetValue<T>(this SerializedProperty property) where T : class
{
try
{
if (property.serializedObject.targetObject == null) return null;
}
catch
{
return null;
}
object obj = property.serializedObject.targetObject;
string path = property.propertyPath.Replace(".Array.data", "");
string[] fieldStructure = path.Split('.');
Regex rgx = new Regex(#"\[\d+\]");
for (int i = 0; i < fieldStructure.Length; i++)
{
if (fieldStructure[i].Contains("["))
{
int index = System.Convert.ToInt32(new string(fieldStructure[i].Where(c => char.IsDigit(c)).ToArray()));
obj = GetFieldValueWithIndex(rgx.Replace(fieldStructure[i], ""), obj, index);
}
else
{
obj = GetFieldValue(fieldStructure[i], obj);
}
}
return (T)obj;
}
public static bool SetValue<T>(this SerializedProperty property, T value) where T : class
{
object obj = property.serializedObject.targetObject;
string path = property.propertyPath.Replace(".Array.data", "");
string[] fieldStructure = path.Split('.');
Regex rgx = new Regex(#"\[\d+\]");
for (int i = 0; i < fieldStructure.Length - 1; i++)
{
if (fieldStructure[i].Contains("["))
{
int index = System.Convert.ToInt32(new string(fieldStructure[i].Where(c => char.IsDigit(c)).ToArray()));
obj = GetFieldValueWithIndex(rgx.Replace(fieldStructure[i], ""), obj, index);
}
else
{
obj = GetFieldValue(fieldStructure[i], obj);
}
}
string fieldName = fieldStructure.Last();
if (fieldName.Contains("["))
{
int index = System.Convert.ToInt32(new string(fieldName.Where(c => char.IsDigit(c)).ToArray()));
return SetFieldValueWithIndex(rgx.Replace(fieldName, ""), obj, index, value);
}
else
{
Debug.Log(value);
return SetFieldValue(fieldName, obj, value);
}
}
private static object GetFieldValue(string fieldName, object obj, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
return field.GetValue(obj);
}
return default(object);
}
private static object GetFieldValueWithIndex(string fieldName, object obj, int index, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
object list = field.GetValue(obj);
if (list.GetType().IsArray)
{
return ((object[])list)[index];
}
else if (list is IEnumerable)
{
return ((IList)list)[index];
}
}
return default(object);
}
public static bool SetFieldValue(string fieldName, object obj, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
field.SetValue(obj, value);
return true;
}
return false;
}
public static bool SetFieldValueWithIndex(string fieldName, object obj, int index, object value, bool includeAllBases = false, BindingFlags bindings = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
{
FieldInfo field = obj.GetType().GetField(fieldName, bindings);
if (field != null)
{
object list = field.GetValue(obj);
if (list.GetType().IsArray)
{
((object[])list)[index] = value;
return true;
}
else if (value is IEnumerable)
{
((IList)list)[index] = value;
return true;
}
}
return false;
}
}
}
I have a property defined in Class A. When the property is changed then i need to raise an event. Another class B should respond to this event and do something. I have done something like this:
Code:
Class A{
string variable = "test";
public delegate void EventHandler(object sender, EventArgs args);
public static event EventHandler ThrowAddEvent = delegate { };
public static event EventHandler ThrowRemoveEvent = delegate { };
public String Name { get; set; }
public int Select { get; set; }
public Window Formref1 { get; set; }
public bool IsSelected
{
get { return IsSlctd; }
set
{
IsSlctd = value;
if (value)
{
ThrowAddEvent(this, new EventArgs());
}
else
{
ThrowRemoveEvent(this, new EventArgs());
}
}
}
}
Another class which is responding to this event is defined as follows:
Class B{
public B(ParsedResult _result)
{
InitializeComponent();
if (_result != null)
{
this.Result = _result;
PopulateColumns1();
DataGrid1.Items.Clear();
DataGrid1.ItemsSource = Populatevariables();
}
}
public void PopulateColumns1()
{
DataGridTextColumn Colvar = new DataGridTextColumn();
Colvar.Header = "Variables";
Colvar.Binding = new Binding("Name");
DataGrid1.Columns.Add(Colvar);
DataGridTemplateColumn Col = new DataGridTemplateColumn();
Col.Header = "Select";
DataTemplate dd = new DataTemplate();
FrameworkElementFactory FF = new FrameworkElementFactory(typeof(CheckBox));
FF.SetBinding(CheckBox.BindingGroupProperty, new Binding("Select"));
FF.SetBinding(CheckBox.IsCheckedProperty, new Binding("IsSelected") { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });
FF.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Center);
dd.VisualTree = FF;
Col.CellTemplate = dd;
DataGrid1.Columns.Add(Col);
}
private List<A> PopulateVariables()
{
CheckBox cb = new CheckBox();
List<A> CharList = new List<A>();
Result.GetCharacteristicList.MoveNext();
IEnumerator<A2LCharacteristic> enumeratorlist = Result.GetCharacteristicList;
for (int i = 0; enumeratorlist.MoveNext(); i++)
{
CharList.Add(new A() { Name = enumeratorlist.Current.Name, Select = i, Formref1 = this});
}
enumeratorlist.Reset();
return CharList;
}
private void OKBtn_Click(object sender, RoutedEventArgs e)
{
A Instance_A = new A();
Instance_A.ThrowAddEvent += (sender1, args) => { Addvrbl(Instance_A.variable); };
Instance_A.ThrowRemoveEvent += (sender2, args) => { RmvVrbl(Instance_A.variable); };
this.Close();
}
public void Addvrbl(string vrbl)
{
if (!(vrbllist.Contains(vrbl)))
{
vrbllist.Add(vrbl);
}
}
public void RmvVrbl(string vrbl)
{
if ((vrbllist.Contains(vrbl)))
{
vrbllist.Remove(vrbl);
}
}
}
The problem is it is not going inside the method "AddVrbl" and "RmvVrbl". I have used the solution from here. Please help.
OK, instead of subscribing to the event of a new instance of A,which will never get triggered/published. When you initializing CharList, you have to subscribe to the event of each A item.
Something like:
for (int i = 0; enumeratorlist.MoveNext(); i++)
{
var obja=new A() { Name = enumeratorlist.Current.Name, Select = i, Formref1 = this};
obja.ThrowAddEvent += ...
obja.ThrowRemoveEvent += ...
CharList.Add(obja);
}
PS: Make sure you un-subscribe these events before removing an item from your CharList.
I have One View which has one Data grid with radio Button , onchecking radio Box , the selected row should go to other View Screen Textbox
here is my first ViewModel
public class CampaignSearchResultsViewModel : ViewModelBase
{
public CampaignSearchResultsViewModel(List<Lead> obj)
{
foreach(Lead lead in obj)
{
SelectedLead = lead;
}
}
public CampaignSearchResultsViewModel()
{
this.Commands.Add("CheckedCommand", new ActionCommand<Lead>(CheckIt));
Commands.Add("OutboundSelect", new ActionCommand<Object>(OutboundSelection));
_leads = new ObservableCollection<Lead>();
}
public ICommand OutboundSelect
{
get
{
return Commands["OutboundSelect"];
}
}
public void OutboundSelection(Object obj)
{
}
private void CheckIt(Lead lead)
{
SelectedLead = lead;
LeadViewModel lmv = new LeadViewModel(this);
}
#region Private
private ObservableCollection<Lead> _leads;
public bool IsChecked { get; set; }
private ICommand _checkedCommand;
private object _testProperty;
private Lead _selectedLead;
private ICollectionView icv;
#endregion
private ICommand _checkedRadioCommand;
private bool _inboundChecked;
#region Properties
public ObservableCollection<Lead> Leads
{
get { return _leads; }
set
{
_leads = value;
FirePropertyChanged("Leads");
}
}
public Lead SelectedLead
{
get { return _selectedLead; }
set { _selectedLead = value; }
}
public ICommand CheckedCommand
{
get
{
return Commands["CheckedCommand"];
}
}
public bool InboundChecked
{
get
{
return _inboundChecked;
}
private set
{
if (_inboundChecked != value)
{
_inboundChecked = value;
FirePropertyChanged("InboundChecked");
}
}
}
#endregion
}
i have to map SelectedLead to the other view model i have pass info to SearchCampaignMembers() method , how
public partial class LeadViewModel : ViewModelBase
{
public void SearchCampaignMembers()
{
_service.Load(_service.SearchCampaignMembersQuery(Entity.FirstName, Entity.LastName), lo =>
{
if (!lo.HasError)
{
ListLead = lo.Entities.ToList();
_savedLeadStatusId = Entity.LeadStatusId;
EntitySet = _service.Leads;
if (ListLead.Count == 1)
{
if (Entity != null)
{
IsVendorLead = Entity.LeadTypeId == Lookups.LeadType.VendorLead;
//Lead Update History
EntityQuery<LeadUpdateHistory> historyquery = null;
historyquery = _service.GetLeadUpdateHistoryByLeadIdQuery(Entity.LeadId);
_service.Load(historyquery, l =>
{
if (!l.HasError)
{
EntityHistory = _service.LeadUpdateHistories;
}
}, null);
//Lead Assignment
EntityQuery<LeadsAssignment> assignmentquery = null;
assignmentquery = _service.GetLeadsAssignmentByLeadIdQuery(Entity.LeadId);
_service.Load(assignmentquery, l =>
{
if (!l.HasError)
{
EntityAssignment = _service.LeadsAssignments;
}
}, null);
if (Entity.LeadTypeId == Lookups.LeadType.PhoneLead)
{
IsInboundLead = Entity.VendorId == null;
IsOutboundLead = Entity.VendorId != null;
}
else
{
IsInboundLead = false;
IsOutboundLead = false;
}
//SelectTimeToCall(Entity);
if (IsOutboundLead)
SelectedCampaign = Entity.LeadCampaigns.FirstOrDefault().Campaign;
else
SelectCampaign(Entity);
OperationsListener listener = new OperationsListener();
listener.Completed += (s, args) =>
{
CompleteInitializing();
//SwitchTab(param.InitialTab);
Action action = () =>
{
SelectDealer(Entity);
};
//GetDealerRecommendation(Entity.Address.ZipCode, action);
SelectStatus(Entity);
//if (callback != null)
// callback();
};
LoadLookupData(listener);
listener.Start();
}
}
else if (ListLead.Count >= 1)
{
CampaignSearchResultsViewModel vm = new CampaignSearchResultsViewModel();
foreach (Lead lead in ListLead)
{
vm.Leads.Add(lead);
ObservableCollection<Lead> abc;
abc = new ObservableCollection<Server.DataAccess.Lead>();
}
ViewController.OpenDialog("SearchCampaignResults", vm, r =>
{
});
}
else if (ListLead.Count == 0)
{
ViewController.OpenDialog("NoResults", (r) =>
{
});
}
}
else
{
//if (callback != null)
// callback();
}
}, null);
}
}
If you use MVVM Light Toolkit, see Messenger class see this answer for sample.
I've just discovered that WPF Markup extension instances are reused in control templates. So each copy of the control template gets the same set of markup extensions.
This doesn't work if you want the extension to maintain some state per control it is attached to. Any idea how to solve this.
Don't store state in the Markup extension. Store it another way. For example.
public abstract class DynamicMarkupExtension : MarkupExtension
{
public class State
{
public object TargetObject { get; set; }
public object TargetProperty { get; set; }
public void UpdateValue(object value)
{
if (TargetObject != null)
{
if (TargetProperty is DependencyProperty)
{
DependencyObject obj = TargetObject as DependencyObject;
DependencyProperty prop = TargetProperty as DependencyProperty;
Action updateAction = () => obj.SetValue(prop, value);
// Check whether the target object can be accessed from the
// current thread, and use Dispatcher.Invoke if it can't
if (obj.CheckAccess())
updateAction();
else
obj.Dispatcher.Invoke(updateAction);
}
else // TargetProperty is PropertyInfo
{
PropertyInfo prop = TargetProperty as PropertyInfo;
prop.SetValue(TargetObject, value, null);
}
}
}
}
public sealed override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
State state = new State();
if (target != null)
{
state.TargetObject = target.TargetObject;
state.TargetProperty = target.TargetProperty;
return ProvideValueInternal(serviceProvider, state);
}
else
{
return null;
}
}
protected abstract object ProvideValueInternal(IServiceProvider serviceProvider, State state);
}
is a base class for handling the type of problem where you need to update the property the markup
extension is attached to at run time. For example a markup extension for binding to ISubject as
<TextBox Text="{Markup:Subscription Path=Excenter, ErrorsPath=Errors}"/>
using the SubscriptionExtension as below. I had had trouble with the code when I used it
within templates but I fixed it so the MarkupExtension did not store state in itself
using ReactiveUI.Ext;
using ReactiveUI.Subjects;
using ReactiveUI.Utils;
using System;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace ReactiveUI.Markup
{
[MarkupExtensionReturnType(typeof(BindingExpression))]
public class SubscriptionExtension : DynamicMarkupExtension
{
[ConstructorArgument("path")]
public PropertyPath Path { get; set; }
[ConstructorArgument("errorsPath")]
public PropertyPath ErrorsPath { get; set; }
public SubscriptionExtension() { }
Maybe<Exception> currentErrorState = Maybe.None<Exception>();
public SubscriptionExtension(PropertyPath path, PropertyPath errorsPath)
{
Path = path;
ErrorsPath = errorsPath;
}
class Proxy : ReactiveObject, IDataErrorInfo, IDisposable
{
string _Value;
public string Value
{
get { return _Value; }
set { this.RaiseAndSetIfChanged(value); }
}
public string Error
{
get { return currentError.Select(e => e.Message).Else(""); }
}
public string this[string columnName]
{
get { return currentError.Select(e => e.Message).Else(""); }
}
public IObservable<Maybe<Exception>> Errors { get; set; }
public Maybe<Exception> currentError = Maybe.None<Exception>();
private CompositeDisposable Subscriptions = new CompositeDisposable();
public Proxy(IObservable<Maybe<Exception>> errors)
{
Errors = errors;
var subscription = errors.Subscribe(e => currentError = e);
Subscriptions.Add(subscription);
}
public void Dispose()
{
Subscriptions.Dispose();
}
}
protected override object ProvideValueInternal(IServiceProvider serviceProvider, DynamicMarkupExtension.State state )
{
var pvt = serviceProvider as IProvideValueTarget;
if (pvt == null)
{
return null;
}
var frameworkElement = pvt.TargetObject as FrameworkElement;
if (frameworkElement == null)
{
return this;
}
DependencyPropertyChangedEventHandler myd = delegate(object sender, DependencyPropertyChangedEventArgs e){
state.UpdateValue(MakeBinding(serviceProvider, frameworkElement));
};
frameworkElement.DataContextChanged += myd;
return MakeBinding(serviceProvider, frameworkElement);
}
private object MakeBinding(IServiceProvider serviceProvider, FrameworkElement frameworkElement)
{
var dataContext = frameworkElement.DataContext;
if (dataContext is String)
{
return dataContext;
}
ISubject<string> subject = Lens.Empty<string>().Subject;
IObservable<Maybe<Exception>> errors = Observable.Empty<Maybe<Exception>>();
Binding binding;
Proxy proxy = new Proxy(errors);
bool madeit = false;
if (dataContext != null)
{
subject = GetProperty<ISubject<string>>(dataContext, Path);
if (subject != null)
{
errors = GetProperty<IObservable<Maybe<Exception>>>
(dataContext
, ErrorsPath) ?? Observable.Empty<Maybe<Exception>>();
proxy = new Proxy(errors);
}
madeit = true;
}
if(!madeit)
{
subject = new BehaviorSubject<string>("Binding Error");
}
// Bind the subject to the property via a helper ( in private library )
var subscription = subject.TwoWayBindTo(proxy, x => x.Value);
// Make sure we don't leak subscriptions
frameworkElement.Unloaded += (e, v) => subscription.Dispose();
binding = new Binding()
{
Source = proxy,
Path = new System.Windows.PropertyPath("Value"),
ValidatesOnDataErrors = true
};
return binding.ProvideValue(serviceProvider);
}
private static T GetProperty<T>(object context, PropertyPath propPath)
where T : class
{
if (propPath==null)
{
return null;
}
try
{
object propValue = propPath.Path
.Split('.')
.Aggregate(context, (value, name)
=> value.GetType()
.GetProperty(name)
.GetValue(value, null));
return propValue as T;
}
catch (NullReferenceException e)
{
throw new MemberAccessException(propPath.Path + " is not available on " + context.GetType(),e);
}
}
}
}
I want to filter a ObservableCollection with max 3000 items in a DataGrid with 6 columns. The user should be able to filter in an "&&"-way all 6 columns.
Should I use LINQ or a CollectionView for it? LINQ seemed faster trying some www samples. Do you have any pro/cons?
UPDATE:
private ObservableCollection<Material> _materialList;
private ObservableCollection<Material> _materialListInternal;
public MaterialBrowserListViewModel()
{
_materialListInternal = new ObservableCollection<Material>();
for (int i = 0; i < 2222; i++)
{
var mat = new Material()
{
Schoolday = DateTime.Now.Date,
Period = i,
DocumentName = "Excel Sheet" + i,
Keywords = "financial budget report",
SchoolclassCode = "1",
};
_materialListInternal.Add(mat);
var mat1 = new Material()
{
Schoolday = DateTime.Now.Date,
Period = i,
DocumentName = "Word Doc" + i,
Keywords = "Economical staticstics report",
SchoolclassCode = "2",
};
_materialListInternal.Add(mat1);
}
MaterialList = CollectionViewSource.GetDefaultView(MaterialListInternal);
MaterialList.Filter = new Predicate<object>(ContainsInFilter);
}
public bool ContainsInFilter(object item)
{
if (String.IsNullOrEmpty(FilterKeywords))
return true;
Material material = item as Material;
if (DocumentHelper.ContainsCaseInsensitive(material.Keywords,FilterKeywords,StringComparison.CurrentCultureIgnoreCase))
return true;
else
return false;
}
private string _filterKeywords;
public string FilterKeywords
{
get { return _filterKeywords; }
set
{
if (_filterKeywords == value)
return;
_filterKeywords = value;
this.RaisePropertyChanged("FilterKeywords");
MaterialList.Refresh();
}
}
public ICollectionView MaterialList { get; set; }
public ObservableCollection<Material> MaterialListInternal
{
get { return _materialListInternal; }
set
{
_materialListInternal = value;
this.RaisePropertyChanged("MaterialList");
}
}
Using ICollectionView gives you automatic collection changed notifications when you call Refresh. Using LINQ you'll need to fire your own change notifications when the filter needs to be re-run to update the UI. Not difficult, but requires a little more thought than just calling Refresh.
LINQ is more flexible that the simple yes/no filtering used by ICollectionView, but if you're not doing something complex there's not really any advantage to that flexibility.
As Henk stated, there shouldn't be a noticable performance difference in the UI.
For an interactive (DataGrid?) experience you should probabaly use the CollectionView. For a more code-oriented sorting, LINQ.
And with max 3000 items, speed should not be a (major) factor in a UI.
How about both? Thomas Levesque built a LINQ-enabled wrapper around ICollectionView.
Usage:
IEnumerable<Person> people;
// Using query comprehension
var query =
from p in people.ShapeView()
where p.Age >= 18
orderby p.LastName, p.FirstName
group p by p.Country;
query.Apply();
// Using extension methods
people.ShapeView()
.Where(p => p.Age >= 18)
.OrderBy(p => p.LastName)
.ThenBy(p => p.FirstName)
.Apply();
Code:
public static class CollectionViewShaper
{
public static CollectionViewShaper<TSource> ShapeView<TSource>(this IEnumerable<TSource> source)
{
var view = CollectionViewSource.GetDefaultView(source);
return new CollectionViewShaper<TSource>(view);
}
public static CollectionViewShaper<TSource> Shape<TSource>(this ICollectionView view)
{
return new CollectionViewShaper<TSource>(view);
}
}
public class CollectionViewShaper<TSource>
{
private readonly ICollectionView _view;
private Predicate<object> _filter;
private readonly List<SortDescription> _sortDescriptions = new List<SortDescription>();
private readonly List<GroupDescription> _groupDescriptions = new List<GroupDescription>();
public CollectionViewShaper(ICollectionView view)
{
if (view == null)
throw new ArgumentNullException("view");
_view = view;
_filter = view.Filter;
_sortDescriptions = view.SortDescriptions.ToList();
_groupDescriptions = view.GroupDescriptions.ToList();
}
public void Apply()
{
using (_view.DeferRefresh())
{
_view.Filter = _filter;
_view.SortDescriptions.Clear();
foreach (var s in _sortDescriptions)
{
_view.SortDescriptions.Add(s);
}
_view.GroupDescriptions.Clear();
foreach (var g in _groupDescriptions)
{
_view.GroupDescriptions.Add(g);
}
}
}
public CollectionViewShaper<TSource> ClearGrouping()
{
_groupDescriptions.Clear();
return this;
}
public CollectionViewShaper<TSource> ClearSort()
{
_sortDescriptions.Clear();
return this;
}
public CollectionViewShaper<TSource> ClearFilter()
{
_filter = null;
return this;
}
public CollectionViewShaper<TSource> ClearAll()
{
_filter = null;
_sortDescriptions.Clear();
_groupDescriptions.Clear();
return this;
}
public CollectionViewShaper<TSource> Where(Func<TSource, bool> predicate)
{
_filter = o => predicate((TSource)o);
return this;
}
public CollectionViewShaper<TSource> OrderBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, true, ListSortDirection.Ascending);
}
public CollectionViewShaper<TSource> OrderByDescending<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, true, ListSortDirection.Descending);
}
public CollectionViewShaper<TSource> ThenBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, false, ListSortDirection.Ascending);
}
public CollectionViewShaper<TSource> ThenByDescending<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
return OrderBy(keySelector, false, ListSortDirection.Descending);
}
private CollectionViewShaper<TSource> OrderBy<TKey>(Expression<Func<TSource, TKey>> keySelector, bool clear, ListSortDirection direction)
{
string path = GetPropertyPath(keySelector.Body);
if (clear)
_sortDescriptions.Clear();
_sortDescriptions.Add(new SortDescription(path, direction));
return this;
}
public CollectionViewShaper<TSource> GroupBy<TKey>(Expression<Func<TSource, TKey>> keySelector)
{
string path = GetPropertyPath(keySelector.Body);
_groupDescriptions.Add(new PropertyGroupDescription(path));
return this;
}
private static string GetPropertyPath(Expression expression)
{
var names = new Stack<string>();
var expr = expression;
while (expr != null && !(expr is ParameterExpression) && !(expr is ConstantExpression))
{
var memberExpr = expr as MemberExpression;
if (memberExpr == null)
throw new ArgumentException("The selector body must contain only property or field access expressions");
names.Push(memberExpr.Member.Name);
expr = memberExpr.Expression;
}
return String.Join(".", names.ToArray());
}
}
Credit:
http://www.thomaslevesque.com/2011/11/30/wpf-using-linq-to-shape-data-in-a-collectionview/
Based on a visual complexity and number of items there really WILL be a noticable performance difference since the Refresh method recreates the whole view!!!
You need my ObservableComputations library. Using this library you can code like this:
ObservableCollection<Material> MaterialList = MaterialListInternal.Filtering(m =>
String.IsNullOrEmpty(FilterKeywords)
|| DocumentHelper.ContainsCaseInsensitive(
material.Keywords, FilterKeywords, StringComparison.CurrentCultureIgnoreCase));
MaterialList reflects all the changes in the MaterialListInternal collection. Do not forget to add the implementation of the INotifyPropertyChanged interface to Material class, so that MaterialList collection reflects the changes in material.Keywords property.