I would like to store usersettings. They are created at runtime and should be read after restarting the application.
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
var property = new SettingsProperty("Testname");
property.DefaultValue = "TestValue";
Settings.Default.Properties.Add(property);
Settings.Default.Save();
}
At this point, the setting is stored and I can access it.
After restarting the application, the newly created setting is away:
public MainForm()
{
InitializeComponent();
foreach (SettingsProperty property in Settings.Default.Properties)
{
//Setting which was created on runtime before not existing
}
}
Trying this piece: Settings.Default.Reload(); didn't affect anything on the outcome. I tried also other things like described here, but neither of them worked for me.
Probably a bit late for you but for others there are 2 parts.
Saving the new UserSetting
Reloading from userConfig.xml at startup
I created this extension for ApplicationSettingsBase based on other answers
public static void Add<T>(this ApplicationSettingsBase settings, string propertyName, T val)
{
var p = new SettingsProperty(propertyName)
{
PropertyType = typeof(T),
Provider = settings.Providers["LocalFileSettingsProvider"],
SerializeAs = SettingsSerializeAs.Xml
};
p.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute());
settings.Properties.Add(p);
settings.Reload();
//finally set value with new value if none was loaded from userConfig.xml
var item = settings[propertyName];
if (item == null)
{
settings[propertyName] = val;
settings.Save();
}
}
This will make Settings["MyKey"] work, but when you restart the setting will not be loaded, but the userConfig.xml has the new value (if you called Settings.Save())
The trick to get it to reload is to execute Add again e.g
if (settings.Properties.Cast<SettingsProperty>().All(s => s.Name != propertyName))
{
settings.Add("MyKey", 0);
};
The way Add works is that it will only set MyKey to 0 if no value is loaded from userConfig.xml
Just a tiny bit later:
I was having the same problem now and the answer of Tom was the only functioning hint. But since it was missing a bit of detail I want to share with you my solution for this.
using System.Configuration;
public static class ApplicationSettingsBaseExtension
{
public static void Add<T>(this ApplicationSettingsBase settings, string propertyName, T val)
{
bool itemExists = false;
foreach (SettingsProperty property in settings.Properties)
{
if (property.Name == propertyName)
{
itemExists = true;
break;
}
}
if (!itemExists)
{
var p = new SettingsProperty(propertyName)
{
PropertyType = typeof(T),
Provider = settings.Providers["LocalFileSettingsProvider"],
SerializeAs = SettingsSerializeAs.Xml
};
p.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute());
settings.Properties.Add(p);
settings.Reload();
}
settings[propertyName] = val;
settings.Save();
}
public static T Get<T>(this ApplicationSettingsBase settings, string propertyName, T defaultValue)
{
bool itemExists = false;
foreach (SettingsProperty property in settings.Properties)
{
if (property.Name == propertyName)
{
itemExists = true;
break;
}
}
if (!itemExists)
{
var p = new SettingsProperty(propertyName)
{
PropertyType = typeof(T),
Provider = settings.Providers["LocalFileSettingsProvider"],
SerializeAs = SettingsSerializeAs.Xml
};
p.Attributes.Add(typeof(UserScopedSettingAttribute), new UserScopedSettingAttribute());
settings.Properties.Add(p);
settings.Reload();
}
//finally set value with new value if none was loaded from userConfig.xml
var item = settings[propertyName];
if (item == null)
{
settings[propertyName] = defaultValue;
settings.Save();
}
return (T)settings[propertyName];
}
}
And with that you can use:
Properties.Settings.Default.Add(settingName, settingValue);
Properties.Settings.Default.Save();
...
setting = Settings.Default.Get(settingName, "");
In my case "setting" was a string, but it should work with all base types.
Note that these settings are not included in Settings.Default.Upgrade().
I hope I could help someone.
Related
I want to check whether the value entered in a DevExpress Control is empty, but my Validation always returns false. For this reason, I am unable to perform further operations.
If you can help I'd appreciate it.
public static class ValidateHelpers {
public static bool validate(Form f, params Control[] control) {
bool result = true;
if (control != null) {
KgsDxValidationProvider prov = new KgsDxValidationProvider { ValidationMode = ValidationMode.Auto };
ConditionValidationRule notEmptyValidationRule = new ConditionValidationRule {
ConditionOperator = ConditionOperator.IsNotBlank,
ErrorText = "You Must Enter A Value",
ErrorType = ErrorType.Critical
};
foreach (var item in control) {
prov.SetValidationRule(item, notEmptyValidationRule);
result = false;
}
f.ValidateChildren();
}
return result;
}
}
You don't need to validate all childrens on your form. This will do what you want:
private bool Validate(params Control[] controls)
{
bool result = controls == null || !controls.Any();
if (controls != null)
{
DXValidationProvider provider = new DXValidationProvider { ValidationMode = ValidationMode.Auto };
ConditionValidationRule noEmptyValues = new ConditionValidationRule
{
ConditionOperator = ConditionOperator.IsNotBlank,
ErrorText = #"You must enter a value",
ErrorType = ErrorType.Critical
};
foreach (Control control in controls)
{
provider.SetValidationRule(control, noEmptyValues);
}
result = provider.Validate(); //Validate all controls associated with the provider
}
return result;
}
If you need further assistance for custom validation etc. you can check this link.
I'm writing a plugin for resharper which I want to use to navigate from a ConcreteCommand -> ConcreteCommandHandler where those types look like this
public class ConcreteCommand : ICommand
public class ConcreteCommandHandler : ICommandHandler<ConcreteCommand>
I've got as far as adding my navigation menu option when the cursor is on a ICommand instance/definition (currently only by checking if the name contains 'Command' and not 'CommandHandler'), and I think I have the code necessary to actually search for a type which inherits something, but my issue is that the only thing I actually have a type for is my ConcereteCommand and I need to create (or get a reference to) the generic type ICommandHandler<T> with T being the type the cursor is currently on.
So I have 2 things I still want to know:
How can I check if my IDeclaredElement is an implementation of a particular interface (ideally by specifying the full name in a string from config)?
How can I create a ITypeElement which is a generic type of a specific interface where I can set the generic type from my existing IDeclaredElements type, so I can then find classes which inherit this?
My existing code looks like this:
[ContextNavigationProvider]
public class CommandHandlerNavigationProvider : INavigateFromHereProvider
{
public IEnumerable<ContextNavigation> CreateWorkflow(IDataContext dataContext)
{
ICollection<IDeclaredElement> declaredElements = dataContext.GetData(DataConstants.DECLARED_ELEMENTS);
if (declaredElements != null || declaredElements.Any())
{
IDeclaredElement declaredElement = declaredElements.First();
if (IsCommand(declaredElement))
{
var solution = dataContext.GetData(JetBrains.ProjectModel.DataContext.DataConstants.SOLUTION);
yield return new ContextNavigation("This Command's &handler", null, NavigationActionGroup.Other, () => { GotToInheritor(solution,declaredElement); });
}
}
}
private void GotToInheritor(ISolution solution, IDeclaredElement declaredElement)
{
var inheritorsConsumer = new InheritorsConsumer();
SearchDomainFactory searchDomainFactory = solution.GetComponent<SearchDomainFactory>();
//How can I create the ITypeElement MyNameSpace.ICommandHandler<(ITypeElement)declaredElement> here?
solution.GetPsiServices().Finder.FindInheritors((ITypeElement)declaredElement, searchDomainFactory.CreateSearchDomain(solution, true), inheritorsConsumer, NullProgressIndicator.Instance);
}
private bool IsCommand(IDeclaredElement declaredElement)
{
//How can I check if my declaredElement is an implementation of ICommand here?
string className = declaredElement.ShortName;
return className.Contains("Command")
&& !className.Contains("CommandHandler");
}
}
Ok managed to work this out with a fair bit of pushing in the right direction from #CitizenMatt.
basically my solution looks like this (still needs some tidying up)
private static readonly List<HandlerMapping> HandlerMappings = new List<HandlerMapping>
{
new HandlerMapping("HandlerNavigationTest.ICommand", "HandlerNavigationTest.ICommandHandler`1", "HandlerNavigationTest"),
new HandlerMapping("HandlerNavTest2.IEvent", "HandlerNavTest2.IEventHandler`1", "HandlerNavTest2")
};
public IEnumerable<ContextNavigation> CreateWorkflow(IDataContext dataContext)
{
ICollection<IDeclaredElement> declaredElements = dataContext.GetData(DataConstants.DECLARED_ELEMENTS);
if (declaredElements != null && declaredElements.Any())
{
IDeclaredElement declaredElement = declaredElements.First();
ISolution solution = dataContext.GetData(JetBrains.ProjectModel.DataContext.DataConstants.SOLUTION);
ITypeElement handlerType = GetHandlerType(declaredElement);
if (handlerType != null)
{
yield return new ContextNavigation("&Handler", null, NavigationActionGroup.Other, () => GoToInheritor(solution, declaredElement as IClass, dataContext, handlerType));
}
}
}
private static ITypeElement GetHandlerType(IDeclaredElement declaredElement)
{
var theClass = declaredElement as IClass;
if (theClass != null)
{
foreach (IPsiModule psiModule in declaredElement.GetPsiServices().Modules.GetModules())
{
foreach (var handlerMapping in HandlerMappings)
{
IDeclaredType commandInterfaceType = TypeFactory.CreateTypeByCLRName(handlerMapping.HandledType, psiModule, theClass.ResolveContext);
ITypeElement typeElement = commandInterfaceType.GetTypeElement();
if (typeElement != null)
{
if (theClass.IsDescendantOf(typeElement))
{
IDeclaredType genericType = TypeFactory.CreateTypeByCLRName(handlerMapping.HandlerType, psiModule, theClass.ResolveContext);
ITypeElement genericTypeElement = genericType.GetTypeElement();
return genericTypeElement;
}
}
}
}
}
return null;
}
private static void GoToInheritor(ISolution solution, IClass theClass, IDataContext dataContext, ITypeElement genericHandlerType)
{
var inheritorsConsumer = new InheritorsConsumer();
var searchDomainFactory = solution.GetComponent<SearchDomainFactory>();
IDeclaredType theType = TypeFactory.CreateType(theClass);
IDeclaredType commandHandlerType = TypeFactory.CreateType(genericHandlerType, theType);
ITypeElement handlerTypeelement = commandHandlerType.GetTypeElement();
solution.GetPsiServices().Finder.FindInheritors(handlerTypeelement, searchDomainFactory.CreateSearchDomain(solution, true),
inheritorsConsumer, NullProgressIndicator.Instance);
var potentialNavigationPoints = new List<INavigationPoint>();
foreach (ITypeElement inheritedInstance in inheritorsConsumer.FoundElements)
{
IDeclaredType[] baseClasses = inheritedInstance.GetAllSuperTypes();
foreach (IDeclaredType declaredType in baseClasses)
{
if (declaredType.IsInterfaceType())
{
if (declaredType.Equals(commandHandlerType))
{
var navigationPoint = new DeclaredElementNavigationPoint(inheritedInstance);
potentialNavigationPoints.Add(navigationPoint);
}
}
}
}
if (potentialNavigationPoints.Any())
{
NavigationOptions options = NavigationOptions.FromDataContext(dataContext, "Which handler do you want to navigate to?");
NavigationManager.GetInstance(solution).Navigate(potentialNavigationPoints, options);
}
}
public class InheritorsConsumer : IFindResultConsumer<ITypeElement>
{
private const int MaxInheritors = 50;
private readonly HashSet<ITypeElement> elements = new HashSet<ITypeElement>();
public IEnumerable<ITypeElement> FoundElements
{
get { return elements; }
}
public ITypeElement Build(FindResult result)
{
var inheritedElement = result as FindResultInheritedElement;
if (inheritedElement != null)
return (ITypeElement) inheritedElement.DeclaredElement;
return null;
}
public FindExecution Merge(ITypeElement data)
{
elements.Add(data);
return elements.Count < MaxInheritors ? FindExecution.Continue : FindExecution.Stop;
}
}
And this allows me no navigate to multiple handlers if they exist. This currently relies on the interfaces for the handled type and the handler type being in the same assembly. But this seems reasonable enough for me at the moment.
I use FlowDocument with BlockUIContainer and InlineUIContainer elements containing (or as base classes) some custom blocks - SVG, math formulas etc.
Because of that using Selection.Load(stream, DataFormats.XamlPackage) wont work as the serialization will drop the contents of *UIContainers except if the Child property is an image as available in Microsoft reference source:
private static void WriteStartXamlElement(...)
{
...
if ((inlineUIContainer == null || !(inlineUIContainer.Child is Image)) &&
(blockUIContainer == null || !(blockUIContainer.Child is Image)))
{
...
elementTypeStandardized = TextSchema.GetStandardElementType(elementType, /*reduceElement:*/true);
}
...
}
The only option in this case is to use is to use XamlWriter.Save and XamlReader.Load which are working flawlessly, serialize and deserialize all required properties and objects of a FlowDocument yet the Copy+Paste must be implemented manually as default implementation of Copy+Paste uses Selection.Load/Save.
Copy/Paste is critical as it is also used to handle dragging of elements in or between RichTextBox controls - the only way objects can be manipulated without custom dragging code.
This is why I am looking to implement copy/paste using a FlowDocument serialization but unfortunately there are some issues with it:
In current solution a whole FlowDocument object needs to be serialized/deserialized. Performance-wise it should not be a problem but I need to store information what selection range needs to be pasted from it (CustomRichTextBoxTag class).
Apparently objects cannot be removed from one document and added to another (a dead-end I discovered recently): 'InlineCollection' element cannot be inserted in a tree because it is already a child of a tree.
[TextElementCollection.cs]
public void InsertAfter(TextElementType previousSibling, TextElementType newItem)
{
...
if (previousSibling.Parent != this.Parent)
throw new InvalidOperationException(System.Windows.SR.Get("TextElementCollection_PreviousSiblingDoesNotBelongToThisCollection", new object[1]
{
(object) previousSibling.GetType().Name
}));
...
}
I think about setting FrameworkContentElement._parent using reflection in all elements which need to be moved to another document but that's a last resort hackish and dirty solution:
In theory I can copy only required objects: (optional) partial run with text at the beginning of selection, all paragraphs and inlines in between and and (possibly) partial run at the end, encapsulate these in a custom class and serialize/deserialize using XamlReader/XamlWriter.
Another solution I didn't think about.
Here is the custom RichTextBox control implementation with partially working custom Copy/Paste code:
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
namespace FlowMathTest
{
public class CustomRichTextBoxTag: DependencyObject
{
public static readonly DependencyProperty SelectionStartProperty = DependencyProperty.Register(
"SelectionStart",
typeof(int),
typeof(CustomRichTextBoxTag));
public int SelectionStart
{
get { return (int)GetValue(SelectionStartProperty); }
set { SetValue(SelectionStartProperty, value); }
}
public static readonly DependencyProperty SelectionEndProperty = DependencyProperty.Register(
"SelectionEnd",
typeof(int),
typeof(CustomRichTextBoxTag));
public int SelectionEnd
{
get { return (int)GetValue(SelectionEndProperty); }
set { SetValue(SelectionEndProperty, value); }
}
}
public class CustomRichTextBox: RichTextBox
{
public CustomRichTextBox()
{
DataObject.AddCopyingHandler(this, OnCopy);
DataObject.AddPastingHandler(this, OnPaste);
}
protected override void OnSelectionChanged(RoutedEventArgs e)
{
base.OnSelectionChanged(e);
var tag = Document.Tag as CustomRichTextBoxTag;
if(tag == null)
{
tag = new CustomRichTextBoxTag();
Document.Tag = tag;
}
tag.SelectionStart = Document.ContentStart.GetOffsetToPosition(Selection.Start);
tag.SelectionEnd = Document.ContentStart.GetOffsetToPosition(Selection.End);
}
private void OnCopy(object sender, DataObjectCopyingEventArgs e)
{
if(e.DataObject != null)
{
e.Handled = true;
var ms = new MemoryStream();
XamlWriter.Save(Document, ms);
e.DataObject.SetData(DataFormats.Xaml, ms);
}
}
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
var xamlData = e.DataObject.GetData(DataFormats.Xaml) as MemoryStream;
if(xamlData != null)
{
xamlData.Position = 0;
var fd = XamlReader.Load(xamlData) as FlowDocument;
if(fd != null)
{
var tag = fd.Tag as CustomRichTextBoxTag;
if(tag != null)
{
InsertAt(Document, Selection.Start, Selection.End, fd, fd.ContentStart.GetPositionAtOffset(tag.SelectionStart), fd.ContentStart.GetPositionAtOffset(tag.SelectionEnd));
e.Handled = true;
}
}
}
}
public static void InsertAt(FlowDocument destDocument, TextPointer destStart, TextPointer destEnd, FlowDocument sourceDocument, TextPointer sourceStart, TextPointer sourceEnd)
{
var destRange = new TextRange(destStart, destEnd);
destRange.Text = string.Empty;
// insert partial text of the first run in the selection
if(sourceStart.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
{
var sourceRange = new TextRange(sourceStart, sourceStart.GetNextContextPosition(LogicalDirection.Forward));
destStart.InsertTextInRun(sourceRange.Text);
sourceStart = sourceStart.GetNextContextPosition(LogicalDirection.Forward);
destStart = destStart.GetNextContextPosition(LogicalDirection.Forward);
}
var field = typeof(FrameworkContentElement).GetField("_parent", BindingFlags.NonPublic | BindingFlags.Instance);
while(sourceStart != null && sourceStart.CompareTo(sourceEnd) <= 0 && sourceStart.Paragraph != null)
{
var sourceInline = sourceStart.Parent as Inline;
if(sourceInline != null)
{
sourceStart.Paragraph.Inlines.Remove(sourceInline);
if(destStart.Parent is Inline)
{
field.SetValue(sourceInline, null);
destStart.Paragraph.Inlines.InsertAfter(destStart.Parent as Inline, sourceInline);
}
else
{
var p = new Paragraph();
destDocument.Blocks.InsertAfter(destStart.Paragraph, p);
p.Inlines.Add(sourceInline);
}
sourceStart = sourceStart.GetNextContextPosition(LogicalDirection.Forward);
}
else
{
var sourceBlock = sourceStart.Parent as Block;
field.SetValue(sourceBlock, null);
destDocument.Blocks.InsertAfter(destStart.Paragraph, sourceBlock);
sourceStart = sourceStart.GetNextContextPosition(LogicalDirection.Forward);
}
}
}
}
}
And the question - is there an existing solution for custom Copy+Paste code for FlowDocument using XamlReader and XamlWriter?
How to fix the code above so it won't complain about different FlowDocument object or work around this limitation?
EDIT: As an experiment I implemented 2) so that objects can be moved from one FlowDocument to another. The code above is updated - all references to the "field" variable.
It seems the bounty period is about to expire and I made a breakthrough how to implement the above problem so I will share it here.
First of all TextRange.Save has a "preserveTextElements" argument which can be used to serialize InlineUIContainer and BlockUIContainer elements. Also, both these controls are not sealed so can be used as base classes for a custom TextElement implementation.
With the above in mind:
I created an InlineMedia element inherited from InlineUIContainer which serializes it's Child "manually" into a "ChildSource" dependency property using XamlReader and XamlWriter and hides the original "Child" from default serializer
I changed the above implementation of CustomRichTextBox to copy selection using range.Save(ms, DataFormats.Xaml, true).
As you can notice, no special Paste handling is necessary as Xaml is nicely deserialized after swapping original Xaml in the clipboard and this means dragging works as copy from all CustomRichtextBox controls and Paste works even into normal RichTextBox.
The only limitation is that for all InlineMedia controls the ChildSource property need to be updated by serializing it's Child before serializing a whole document and I found no way to do it automatically (hook into TextRange.Save before element is saved).
I can live with that but a nicer solution without this problem will still get a bounty!
InlineMedia element code:
public class InlineMedia: InlineUIContainer
{
public InlineMedia()
{
}
public InlineMedia(UIElement childUIElement) : base(childUIElement)
{
UpdateChildSource();
}
public InlineMedia(UIElement childUIElement, TextPointer insertPosition)
: base(childUIElement, insertPosition)
{
UpdateChildSource();
}
public static readonly DependencyProperty ChildSourceProperty = DependencyProperty.Register
(
"ChildSource",
typeof(string),
typeof(InlineMedia),
new FrameworkPropertyMetadata(null, OnChildSourceChanged));
public string ChildSource
{
get
{
return (string)GetValue(ChildSourceProperty);
}
set
{
SetValue(ChildSourceProperty, value);
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new UIElement Child
{
get
{
return base.Child;
}
set
{
base.Child = value;
UpdateChildSource();
}
}
public void UpdateChildSource()
{
IsInternalChildSourceChange = true;
try
{
ChildSource = Save();
}
finally
{
IsInternalChildSourceChange = false;
}
}
public string Save()
{
if(Child == null)
{
return null;
}
using(var stream = new MemoryStream())
{
XamlWriter.Save(Child, stream);
stream.Position = 0;
using(var reader = new StreamReader(stream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
public void Load(string sourceData)
{
if(string.IsNullOrEmpty(sourceData))
{
base.Child = null;
}
else
{
using(var stream = new MemoryStream(Encoding.UTF8.GetBytes(sourceData)))
{
var child = XamlReader.Load(stream);
base.Child = (UIElement)child;
}
}
}
private static void OnChildSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var img = (InlineMedia) sender;
if(img != null && !img.IsInternalChildSourceChange)
{
img.Load((string)e.NewValue);
}
}
protected bool IsInternalChildSourceChange { get; private set; }
}
CustomRichTextBox control code:
public class CustomRichTextBox: RichTextBox
{
public CustomRichTextBox()
{
DataObject.AddCopyingHandler(this, OnCopy);
}
private void OnCopy(object sender, DataObjectCopyingEventArgs e)
{
if(e.DataObject != null)
{
UpdateDocument();
var range = new TextRange(Selection.Start, Selection.End);
using(var ms = new MemoryStream())
{
range.Save(ms, DataFormats.Xaml, true);
ms.Position = 0;
using(var reader = new StreamReader(ms, Encoding.UTF8))
{
var xaml = reader.ReadToEnd();
e.DataObject.SetData(DataFormats.Xaml, xaml);
}
}
e.Handled = true;
}
}
public void UpdateDocument()
{
ObjectHelper.ExecuteRecursive<InlineMedia>(Document, i => i.UpdateChildSource(), FlowDocumentVisitors);
}
private static readonly Func<object, object>[] FlowDocumentVisitors =
{
x => (x is FlowDocument) ? ((FlowDocument) x).Blocks : null,
x => (x is Section) ? ((Section) x).Blocks : null,
x => (x is BlockUIContainer) ? ((BlockUIContainer) x).Child : null,
x => (x is InlineUIContainer) ? ((InlineUIContainer) x).Child : null,
x => (x is Span) ? ((Span) x).Inlines : null,
x => (x is Paragraph) ? ((Paragraph) x).Inlines : null,
x => (x is Table) ? ((Table) x).RowGroups : null,
x => (x is Table) ? ((Table) x).Columns : null,
x => (x is Table) ? ((Table) x).RowGroups.SelectMany(rg => rg.Rows) : null,
x => (x is Table) ? ((Table) x).RowGroups.SelectMany(rg => rg.Rows).SelectMany(r => r.Cells) : null,
x => (x is TableCell) ? ((TableCell) x).Blocks : null,
x => (x is TableCell) ? ((TableCell) x).BorderBrush : null,
x => (x is List) ? ((List) x).ListItems : null,
x => (x is ListItem) ? ((ListItem) x).Blocks : null
};
}
and finally ObjectHelper class - a visitor helper:
public static class ObjectHelper
{
public static void ExecuteRecursive(object item, Action<object> execute, params Func<object, object>[] childSelectors)
{
ExecuteRecursive<object, object>(item, null, (c, i) => execute(i), childSelectors);
}
public static void ExecuteRecursive<TObject>(object item, Action<TObject> execute, params Func<object, object>[] childSelectors)
{
ExecuteRecursive<object, TObject>(item, null, (c, i) => execute(i), childSelectors);
}
public static void ExecuteRecursive<TContext, TObject>(object item, TContext context, Action<TContext, TObject> execute, params Func<object, object>[] childSelectors)
{
ExecuteRecursive(item, context, (c, i) =>
{
if(i is TObject)
{
execute(c, (TObject)i);
}
}, childSelectors);
}
public static void ExecuteRecursive<TContext>(object item, TContext context, Action<TContext, object> execute, params Func<object, object>[] childSelectors)
{
execute(context, item);
if(item is IEnumerable)
{
foreach(var subItem in item as IEnumerable)
{
ExecuteRecursive(subItem, context, execute, childSelectors);
}
}
if(childSelectors != null)
{
foreach(var subItem in childSelectors.Select(x => x(item)).Where(x => x != null))
{
ExecuteRecursive(subItem, context, execute, childSelectors);
}
}
}
}
1.In current solution a whole FlowDocument object needs to be serialized/deserialized. Performance-wise it should not be a problem
but I need to store information what selection range needs to be
pasted from it (CustomRichTextBoxTag class).
This smells like an opportunity to use an attached property based on the intended behavior you identified. I understand attached properties as a way of adding additional behavior to an element. When you register an attached property, you can add an event handler for when that property value changes.
To take advantage of this, I would wire this attached property to a DataTrigger to update the selection range value for your copy/paste operation.
2.Apparently objects cannot be removed from one document and added to another (a dead-end I discovered recently): 'InlineCollection' element
cannot be inserted in a tree because it is already a child of a tree.
You can get around this by constructing your elements programmatically and also removing your elements programmatically. At the end of the day, you're mainly dealing with either an ItemsControl or a ContentControl. In this case your working with an ItemsControl (i.e. document). As a result just add and remove child elements from your ItemsControl (document) programmatically.
Say I have a BindingList<Person> where Person has a public string property called Name. Is there a way to effectively (if not directly) bind to the Current property (which is a Person object) and then index into the Name property?
I'm imagining a binding set up like
nameLabel.DataBindings.Add(
new Binding("Text", this.myBindingListSource, "Current.Name", true));
or
nameLabel.DataBindings.Add(
new Binding("Text", this.myBindingListSource.Current, "Name", true));
Both of these approaches produce runtime errors.
Currently, I am simply subscribing to the BindingList's CurrentChanged event and handling updates in there. This works but I would prefer the DataBinding approach if possible.
Using the NestedBindingProxy class provided below, you can do
nameLabel.DataBindings.Add(
new Binding("Text", new NestedBindingProxy(this.myBindingListSource, "Current.Name"), true));
Below is the c# code for NestedBindingProxy. The problem with WinForms data binding is it doesn't detect value changes when you use a navigation path that contains multiple properties. WPF does this though. So I created the class NestedBindingProxy that does the change detection and it exposes a property called "Value" that the windows binding can bind too. Whenever any property changes in the navigation path, the notify property changed event will fire for the "Value" property.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
namespace WindowsFormsApplication4
{
public sealed class NestedBindingProxy : INotifyPropertyChanged
{
class PropertyChangeListener
{
private readonly PropertyDescriptor _prop;
private readonly WeakReference _prevOb = new WeakReference(null);
public event EventHandler ValueChanged;
public PropertyChangeListener(PropertyDescriptor property)
{
_prop = property;
}
public object GetValue(object obj)
{
return _prop.GetValue(obj);
}
public void SubscribeToValueChange(object obj)
{
if (_prop.SupportsChangeEvents)
{
_prop.AddValueChanged(obj, ValueChanged);
_prevOb.Target = obj;
}
}
public void UnsubsctribeToValueChange()
{
var prevObj = _prevOb.Target;
if (prevObj != null)
{
_prop.RemoveValueChanged(prevObj, ValueChanged);
_prevOb.Target = null;
}
}
}
private readonly object _source;
private PropertyChangedEventHandler _subscribers;
private readonly List<PropertyChangeListener> _properties = new List<PropertyChangeListener>();
private readonly SynchronizationContext _synchContext;
public event PropertyChangedEventHandler PropertyChanged
{
add
{
bool hadSubscribers = _subscribers != null;
_subscribers += value;
bool hasSubscribers = _subscribers != null;
if (!hadSubscribers && hasSubscribers)
{
ListenToPropertyChanges(true);
}
}
remove
{
bool hadSubscribers = _subscribers != null;
_subscribers -= value;
bool hasSubscribers = _subscribers != null;
if (hadSubscribers && !hasSubscribers)
{
ListenToPropertyChanges(false);
}
}
}
public NestedBindingProxy(object source, string nestedPropertyPath)
{
_synchContext = SynchronizationContext.Current;
_source = source;
var propNames = nestedPropertyPath.Split('.');
Type type = source.GetType();
foreach (var propName in propNames)
{
var prop = TypeDescriptor.GetProperties(type)[propName];
var propChangeListener = new PropertyChangeListener(prop);
_properties.Add(propChangeListener);
propChangeListener.ValueChanged += (sender, e) => OnNestedPropertyChanged(propChangeListener);
type = prop.PropertyType;
}
}
public object Value
{
get
{
object value = _source;
foreach (var prop in _properties)
{
value = prop.GetValue(value);
if (value == null)
{
return null;
}
}
return value;
}
}
private void ListenToPropertyChanges(bool subscribe)
{
if (subscribe)
{
object value = _source;
foreach (var prop in _properties)
{
prop.SubscribeToValueChange(value);
value = prop.GetValue(value);
if (value == null)
{
return;
}
}
}
else
{
foreach (var prop in _properties)
{
prop.UnsubsctribeToValueChange();
}
}
}
private void OnNestedPropertyChanged(PropertyChangeListener changedProperty)
{
ListenToPropertyChanges(false);
ListenToPropertyChanges(true);
var subscribers = _subscribers;
if (subscribers != null)
{
if (_synchContext != SynchronizationContext.Current)
{
_synchContext.Post(delegate { subscribers(this, new PropertyChangedEventArgs("Value")); }, null);
}
else
{
subscribers(this, new PropertyChangedEventArgs("Value"));
}
}
}
}
}
Try this:
Binding bind = new Binding("Text", myBindingListSource, "Current");
bind.Format += (s,e) => {
e.Value = e.Value == null ? "" : ((Person)e.Value).Name;
};
nameLabel.DataBindings.Add(bind);
I haven't tested it but it should work and I've been waiting for the feedback from you.
I stumbled across this by accident (four years after the original post), and after a quick read, I find that the accepted answer appears to be really over-engineered in respect to the OP's question.
I use this approach, and have posted an answer here to (hopefully) help anyone else who has the same problem.
The following should work (if infact this.myBindingListSource implements IBindingList)
I would just create a binding source to wrap my binding list (in my Form/View)
BindingSource bindingSource = new BindingSource(this.myBindingListSource, string.Empty);
And then just bind to the binding source like this:
nameLabel.DataBindings.Add(new Binding("Text", bindingSource, "Name"));
I think the OP's original code didn't work because there is no member called 'Current' on a BindingList (unless the OP has some sort of specialised type not mentioned).
Asp .net MVC 3 application...
This is the View:
Grupa: <%= Html.DropDownListFor(x => x.Grupa, Model.ListaGrupe) %>
Produsul: <%= Html.DropDownListFor(x => x.Produs, Model.ListaProduse) %>
Cantitate: <%=Html.TextBoxFor(x => x.Cantitate, new { style = "width: 100px;" })%>
Pret: <%=Html.TextBoxFor(x => x.Pret, new { style = "width: 100px;", disabled = true})%>
TVA: <%= Html.TextBoxFor(x => x.TVA, new { style = "width: 100px;", disabled = true })%>
Valoare: <%= Html.TextBoxFor(x => x.NoTVA, new { style = "width: 120px;", disabled = true})%>
Valoare cu TVA: <%=Html.TextBoxFor(x => x.Total, new { style = "width: 120px;", disabled = true})%>
I am using some JQuery to change Pret, TVA, NoTVA and Total based on the values in Grupa, Produs and Cantitate so I don't want the user to modify the values inside them.
Probably disabled = true shoudn't be used. Then how can I make so the user can't modify the fields but the value to be posted to the controller's action?
You can also make them readonly rather than disabling them. On the other note, I think #Chris solution is better, that way your modified data will be posted back.
You can use Html.HiddenFor() and use a <span> or <div> instead. Their values will then be posted back.
Well, this is what i did up to now,
i didn't succeed to make a good, easy to use, readonly protection using encryption,
but i did manage to do something that i think might just do.
how it works:
When you use LockObject(o) an object, itterate the properties that have defined ProtectedAttribute defined for.
add the locked value to a list, specially made for this field.
! the list is kept in the user session (on the server side)
when the user submits the form, IsValid checks to see if the value is in the list of locked values. if yes, then it is all ok. otherwise, it must have been changed somehow.
! the number of values is not that big, and is temporary to the session, but if it is bothering someone, a simple lockList.remove(node); can easly be added when a value is validated.
Note: this can cause problem when the user uses Back buttons or Resubmit a form using Refresh.
tell me if you find any problems that this model does not take into account...
+ the Equalization is very naive, so it works only with value-types for time be.
Code:
Created an attribute named ProtectedAttribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class ProtectedPropertyAttribute : ValidationAttribute
{
private static Dictionary<string, LinkedList<object>> savedValues;
static ProtectedPropertyAttribute()
{
savedValues = (Dictionary<string, LinkedList<object>>)HttpContext.Current.Session["ProtectedAttributeData"];
if (savedValues != null)
return;
savedValues = new Dictionary<string, LinkedList<object>>();
HttpContext.Current.Session.Add("ProtectedAttributeData", savedValues);
}
public static void LockObject(object obj)
{
Type type = obj.GetType();
foreach (PropertyInfo property in type.GetProperties())
{
LockProperty(obj, property);
}
}
public static void LockProperty(object obj, PropertyInfo property)
{
ProtectedPropertyAttribute protectedAttribute =
(ProtectedPropertyAttribute)
property.GetCustomAttributes(typeof (ProtectedPropertyAttribute), false).FirstOrDefault();
if (protectedAttribute == null)
return;
if(protectedAttribute.Identifier == null)
protectedAttribute.Identifier = property.Name;
LinkedList<object> list;
if (!savedValues.TryGetValue(protectedAttribute.Identifier, out list))
{
list = new LinkedList<object>();
savedValues.Add(protectedAttribute.Identifier, list);
}
list.AddLast(property.GetValue(obj, null));
}
public string Identifier { get; set; }
public ProtectedPropertyAttribute()
{
}
public ProtectedPropertyAttribute(string errorMessage) : base(errorMessage)
{
}
public ProtectedPropertyAttribute(Func<string> errorMessageAccessor) : base(errorMessageAccessor)
{
}
protected override ValidationResult IsValid (object value, ValidationContext validationContext)
{
LinkedList<object> lockedValues;
if (Identifier == null)
Identifier = validationContext.DisplayName;
if (!savedValues.TryGetValue(Identifier, out lockedValues))
return new ValidationResult(FormatErrorMessage(validationContext.MemberName), new[] { validationContext.MemberName });
bool found = false;
LinkedListNode<object> node = lockedValues.First;
while (node != null)
{
if(node.Value.Equals(value))
{
found = true;
break;
}
node = node.Next;
}
if(!found)
return new ValidationResult(FormatErrorMessage(validationContext.MemberName), new[] { validationContext.MemberName });
return ValidationResult.Success;
}
}
place this attribute on any property of your model just as any other validation.
public class TestViewModel : Controller
{
[ProtectedProperty("You changed me. you bitch!")]
public string DontChangeMe { get; set; }
public string ChangeMe { get; set; }
}
in the controller, after you are finished with the viewmodel object,
you call ProtectedAttribute.LockObject(myViewModel)
public class TestController : Controller
{
public ActionResult Index()
{
TestViewModel vm = new TestViewModel {ChangeMe = "a1", DontChangeMe = "b1"};
ProtectedPropertyAttribute.LockObject(vm);
return View(vm);
}
public string Submit(TestViewModel vm)
{
string errMessage;
return !validate(out errMessage) ? "you are a baaad, man." + errMessage : "you are o.k";
}
private bool validate(out string errormessage)
{
if (ModelState.IsValid)
{
errormessage = null;
return true;
}
StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<string, ModelState> pair in ModelState)
{
sb.Append(pair.Key);
sb.Append(" : <br/>");
foreach (ModelError err in pair.Value.Errors)
{
sb.Append(" - ");
sb.Append(err.ErrorMessage);
sb.Append("<br/>");
}
sb.Append("<br/>");
}
errormessage = sb.ToString();
return false;
}
}