A custom attribute for setting whether a textbox is editable - c#

I am trying to setup a custom attribute in C# to set whether a business object property is editable, eventually enabling or disabling ReadOnly a textbox in XAML. Since (I thought) IsEditable was already implemented in System.Windows.Controls, i thought this would work:
[AttributeUsage(AttributeTargets.Property)]
public class EditableAttribute : Attribute
{
public EditableAttribute(bool isEditable)
{
this.ReadOnly = !isEditable;
}
public virtual bool ReadOnly { get; set; }
}
Well, go figure, it doesn't. I set [Editable(false)] to a string in an object and it is still editable. I have a feeling I'm not even close. Any help or suggestions would be greatly appreciated!
I am aware this can be setup as a style in xaml, but for this case it needs to be in the business object.
Thanks

You can use BindingDecoratorBase to use custom binding and use an attribute.
The following code is just me modifying my code in my project that uses custom validation . It probably should be refractored.
public interface IEditatble
{
void SetValue(Control sender, DependencyProperty property);
}
[AttributeUsage(AttributeTargets.Property)]
public class EditableAttribute : Attribute, IEditatble
{
public EditableAttribute(bool isEditable)
{
this.ReadOnly = !isEditable;
}
public virtual bool ReadOnly { get; set; }
public void SetValue(System.Windows.Controls.Control sender, System.Windows.DependencyProperty property)
{
sender.SetValue(property, this.ReadOnly);
}
}
You can create a custom binding:
public class ReadonlyBinding : BindingDecoratorBase
{
private DependencyProperty _targetProperty = null;
public ReadonlyBinding()
: base()
{
Binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
}
public override object ProvideValue(IServiceProvider provider)
{
// Get the binding expression
object bindingExpression = base.ProvideValue(provider);
// Bound items
DependencyObject targetObject;
// Try to get the bound items
if (TryGetTargetItems(provider, out targetObject, out _targetProperty))
{
if (targetObject is FrameworkElement)
{
// Get the element and implement datacontext changes
FrameworkElement element = targetObject as FrameworkElement;
element.DataContextChanged += new DependencyPropertyChangedEventHandler(element_DataContextChanged);
}
}
// Go on with the flow
return bindingExpression;
}
void element_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
object datacontext = e.NewValue;
if (datacontext != null && _targetProperty != null)
{
PropertyInfo property = datacontext.GetType().GetProperty(Binding.Path.Path);
if (property != null)
{
var attribute = property.GetCustomAttributes(true).Where(o => o is IEditatble).FirstOrDefault();
if (attribute != null)
{
Control cntrl = sender as Control;
((IEditatble)attribute).SetValue(cntrl, _targetProperty);
}
}
}
}
}
And you can use it like:
[Editable(true)]
public string Name { get; set; }
Xaml:
<TextBox IsReadOnly="{local:ReadonlyBinding Path=Name}" />

For your EditableAttribute to work, TextBox classes should use reflection on your model to check whether the attribute is set and set necessary properties. What I'm trying to say is that attribute is no more than metadata and it doesn't control the application workflow unless the application wishes so.
You could inherit from basic TextBox and insert necessary functionality though it is an overkill. You should just declare IsSomePropertyReadOnly variable and bind to it in TextBox.
Though if you're feeling really fancy, you could write some wrapper class like
public class ReadOrWriteText<T>
{
private T _value;
bool IsReadOnly { get; set; }
public T Value
{
get { return _value; }
set { if (IsReadOnly) return; _value = value; }
}
}
and bind to it's IsReadOnly and Value properties. Though it is an overkill also.

Related

Conditional settings in WPF application

I'm making user changeable settings for my media player and I'm struggling to find an elegant solution to the problem.
One of my settings for example - pauses the video at it's last frame, if not checked it will either continue through the playlist or if it's only 1 file, reset it and pause it at the start.
This is how I've implemented it:
private void OnMediaEndedCommand()
{
if (GeneralSettings.PauseOnLastFrame)
{
MediaPlayer.SetMediaState(MediaPlayerStates.Pause);
return;
}
if (PlayListViewModel.FilesCollection.Last().Equals(PlayListViewModel.FilesCollection.Current) && !Repeat)
{
ChangeMediaPlayerSource(PlayListViewModel.ChangeCurrent(() => PlayListViewModel.FilesCollection.MoveNext()));
MediaPlayer.SetMediaState(MediaPlayerStates.Stop);
return;
}
ChangeMediaPlayerSource(PlayListViewModel.ChangeCurrent(() => PlayListViewModel.FilesCollection.MoveNext()));
}
This is contained inside the ViewModel of the main window, where the media element is and GeneralSettings.PauseOnLastFrame is a boolean property.
This command is binded as follows:
<MediaElement ....>
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="MediaEnded">
<ia:InvokeCommandAction Command="{Binding MediaEndedCommand}"/>
</ia:EventTrigger>
</ia:Interaction.Triggers>
</MediaElement>
It works but it's awful, how should I go about implementing such setting system in an elegant way? Some settings might not be boolean, they might have multiple options, some might be applied only on startup and others, as the one illustrated above, event based.
Based on the information and sample code you provided, I would suggest
Approach - 1
A tightly couple ViewModel with System.Configuration.ApplicationSettingsBase and you can mention all you properties in ViewModel and map single of them with a separate application setting property. You can use your settings directly in biding afterwards e.g. : {x:Static Settings.Default.Whatevs}. Othe "Save" button click event or main window close event, you can save all you settings e.g. : Settings.Default.Save();
Approach - 2
A better approach, I would suggest / prefer (if I am developing this app) is to develop a wrapper class (e.g.: SettingProvider) that implement an inheritance (e.g: ISettingProvider) which uncovers all you settings as separate properties and also have a save method which saves all setting values. You can use this wrapper class into your ViewModel to handle all the commands and setting values in better way.
The benefit of this approach is the if you decide to change you setting to database , you need not to make change to you ViewModel as all job is done in SettingProvider class.
I am not sure but based on viewing your code, I assume that you used Approach-1. Please put you comments and any feedback to this answer. I would like to know what you think and may be you have got more simple and interesting way of achieving this.
UPDATE-1
Example
Enum for showing you demo
public enum MediaStatus
{
Playing = 0,
Stopped = 1,
Paused = 2
}
Interface
public interface ISettingProvider
{
double Volumne { get; set; }
string LastMediaUrl { get; set; }
MediaStatus PlayingMediaStatus;
void SaveSettings();
}
Wrapper Class
public class SettingProvider : ISettingProvider
{
private double volumne;
public double Volumne // read-write instance property
{
get
{
return volumne;
}
set
{
volumne = value;
Settings.Default.Volumne = volumne;
}
}
private string lastMediaUrl;
public string LastMediaUrl // read-write instance property
{
get
{
return lastMediaUrl;
}
set
{
lastMediaUrl = value;
Settings.Default.LastMediaUrl = lastMediaUrl;
}
}
private MediaStatus playingMediaStatus;
public MediaStatus PlayingMediaStatus // read-write instance property
{
get
{
return playingMediaStatus;
}
set
{
playingMediaStatus = value;
Settings.Default.PlayingMediaStatus = (int)playingMediaStatus;
}
}
public void SaveSettings()
{
Settings.Default.Save();
}
//Constructor
public SettingProvider()
{
this.Volumne = Settings.Default.Volumne;
this.LastMediaUrl = Settings.Default.LastMediaUrl;
this.PlayingMediaStatus = (MediaStatus)Settings.Default.PlayingMediaStatus;
}
}
ViewModelBase Class
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
CommandHandler Class
public class CommandHandler : ICommand
{
public event EventHandler CanExecuteChanged { add { } remove { } }
private Action<object> action;
private bool canExecute;
public CommandHandler(Action<object> action, bool canExecute)
{
this.action = action;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute;
}
public void Execute(object parameter)
{
action(parameter);
}
}
ViewModel
public class SettingsViewModel : ViewModelBase
{
SettingProvider objSettingProvider = new SettingProvider();
public double Volumne
{
get
{
return objSettingProvider.Volumne;
}
set
{
objSettingProvider.Volumne = value;
OnPropertyChanged("Volumne");
}
}
// Implementaion of other properties of SettingProvider with your ViewModel properties;
private ICommand saveSettingButtonCommand;
public ICommand SaveSettingButtonCommand
{
get
{
return saveSettingButtonCommand ?? (saveSettingButtonCommand = new CommandHandler(param => saveSettings(param), true));
}
}
private void saveSettings()
{
objSettingProvider.SaveSettings();
}
}
UPDATE-2
public interface ISettingProvider
{
bool PauseOnLastFrame;
bool IsAutoPlay;
MediaStatus PlayingMediaStatus;
void SaveSettings();
}
public class SettingProvider : ISettingProvider
{
private bool pauseOnLastFrame;
public bool PauseOnLastFrame // read-write instance property
{
get
{
return pauseOnLastFrame;
}
set
{
pauseOnLastFrame = value;
Settings.Default.PauseOnLastFrame = volumne;
}
}
private bool isAutoPlay;
public bool IsAutoPlay // read-write instance property
{
get
{
return isAutoPlay;
}
set
{
isAutoPlay = value;
Settings.Default.IsAutoPlay = volumne;
}
}
}
public class SettingsViewModel : ViewModelBase
{
SettingProvider objSettingProvider = new SettingProvider();
MediaStatus PlayingMediaStatus
{
get
{
return objSettingProvider.PlayingMediaStatus;
}
set
{
if(value == MediaStatus.Paused)
MediaPlayer.Pause();
if(value == MediaStatus.Playing)
MediaPlayer.Play();
if(value == MediaStatus.Stopped)
MediaPlayer.Stop();
objSettingProvider.PlayingMediaStatus = (int)value;
OnPropertyChanged("PlayingMediaStatus");
}
}
private string currentMediaFile;
public string CurrentMediaFile
{
get
{
return currentMediaFile;
}
set
{
currentMediaFile = value;
MediaPlayer.Stop();
MediaPlayer.Current = currentMediaFile;
if(objSettingProvider.IsAutoPlay)
MediaPlayer.Play();
OnPropertyChanged("CurrentMediaFile");
}
}
// Implementaion of other properties of SettingProvider with your ViewModel properties;
private ICommand onMediaEndedCommand;
public ICommand OnMediaEndedCommand
{
get
{
return onMediaEndedCommand ?? (onMediaEndedCommand = new CommandHandler(param => onMediaEnded(param), true));
}
}
private void onMediaEnded()
{
if(objSettingProvider.PauseOnLastFrame)
{
PlayingMediaStatus = MediaStatus.Paused;
}
else if(PlayListViewModel.FilesCollection.Last().Equals(PlayListViewModel.FilesCollection.Current) && !Repeat)
{
PlayingMediaStatus = MediaStatus.Stopped;
}
else
{
CurrentMediaFile = PlayListViewModel.FilesCollection.MoveNext();
}
}
}
NOTE: This is the detailed example I put here and also avoid some syntax error or naming error if I missed somewhere. Please correct it.
I am not aware which media player settings you are using. I took some sample properties. This is just an example of structure you can implement for you application. You may need to alter more code to implement this structure.
An elegant way to implement this IMHO would be to use a dependency injection container, this will provide great flexibility while allowing you to completely separate concerns (i.e. the settings implementation from your view models and custom controls).
There are many DI frameworks out there, for my example I will use simple injector because it is free (open source), simple and fast but you can apply the same principle to other frameworks (Unity, Ninject, etc..).
Start by creating an interface for your settings service, for example:
public interface ISettingsService
{
double Volumne { get; set; }
string LastMediaUrl { get; set; }
MediaStatus PlayingMediaStatus;
void SaveSettings();
}
Then add your implementation for the service, the beauty of using DI is that you can change the implementation at anytime or completely replace it and your application will continue to work as usual.
Let's say you want to use application settings, here is your service:
public class SettingsServiceFromApplication : ISettingsService
{
public double Volume
{
get
{
return Properties.Settings.Volume;
}
}
[...]
}
Or let's say you want to use a database to store your settings:
public class SettingsServiceFromDb : ISettingsService
{
public double Volume
{
get
{
return MyDb.Volumen;
}
}
[...]
}
Then you can use a DI container to specify which implementation to use:
Start by installing the library using NuGet:
Install-Package SimpleInjector -Version 4.0.12
You need a way to share your container throughout the application, I usually just go with a static class that I initialize when starting the app:
using Container = SimpleInjector.Container;
namespace YourNamespace
{
public class Bootstrapper
{
internal static Container Container;
public static void Setup()
{
//Create container and register services
Container = new Container();
//Let's specify that we want to use SettingsServiceFromApplication
Container.Register<ISettingsService, SettingsServiceFromApplication>();
//You can use your bootstrapper class to initialize other stuff
}
}
You need to call Setup when starting the App, the best place is in the App constructor:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper.Setup();
}
}
So now you have an app wide depedency injection container that you can use to request "services" (specific implementations of an interface).
To get the settings implementation in your view models you could simply call the container as follows:
// This will return an instance of SettingsServiceFromApplication
ISettingsService settingsService = Bootstrapper.Container.GetInstance<ISettingsService>();
double volumen = settingsService.Volume;
To make it easier to work with, I usually create a base view model that will allow to get services more easyly, for example:
public abstract BaseViewModel
{
private ISettingsService _settings;
protected ISettingsService GeneralSettings
{
get
{
if (_settings == null)
_settings = Bootstrapper.Container.GetInstance<ISettingsService>();
return _settings;
}
}
}
Every view model inheriting from this class will have access to the settings:
public class YourViewModel : BaseViewModel
{
private void OnMediaEndedCommand()
{
if (GeneralSettings.PauseOnLastFrame)
{
MediaPlayer.SetMediaState(MediaPlayerStates.Pause);
return;
}
if (PlayListViewModel.FilesCollection.Last().Equals(PlayListViewModel.FilesCollection.Current) && !Repeat)
{
ChangeMediaPlayerSource(PlayListViewModel.ChangeCurrent(() => PlayListViewModel.FilesCollection.MoveNext()));
MediaPlayer.SetMediaState(MediaPlayerStates.Stop);
return;
}
ChangeMediaPlayerSource(PlayListViewModel.ChangeCurrent(() => PlayListViewModel.FilesCollection.MoveNext()));
}
}
As you can see the code is the same as your code! But now the settings are coming from your container. Where is the elegance? Well, let's say that one year from now someone decides that you will store your settings in a database, what do you need to change in your code?
Container.Register<ISettingsService, SettingsServiceFromDb>();
A single line. Everything else should work as usual.
As well as view models, you could use this mechanism in your own controls:
public class MyMediaElement : UserControl //Or MediaElement and instead of commands you can override real events in the control code behind, this does not break the MVVM pattern at all, just make sure you use depedency properties if you need to exchange data with your view models
{
private void OnMediaEndedCommand()
{
//Get your settings from your container, do whatever you want to do depending on the settings
[...]
}
}
Then just use your control in your Views / ViewModels:
<local:MyMediaElement />
Yep, that's all you need because you handle everything in your User / Custom control, your view models doesn't need to care about how you handle settings in the control.
There are many options you can use to register containers, I recommend you take a look at the docs:
https://simpleinjector.org/index.html
https://simpleinjector.readthedocs.io/en/latest/index.html
I think maybe you are looking for an interface approach?
public interface IMediaEndedHandler
{
bool AlternateHandling(MediaPlayer player);
}
public class NullMediaEndedHandler : IMediaEndedHandler
{
public bool AlternateHandling(MediaPlayer player)
{
return false;
}
}
public class PauseOnLastFrameHandler : IMediaEndedHandler
{
public bool AlternateHandling(MediaPlayer player)
{
player.SetMediaState(MediaPlayerStates.Pause);
return true;
}
}
public class GeneralSettings
{
private bool pauseOnLastFrame;
private bool PauseOnLastFrame
{
get
{
return pauseOnLastFrame;
}
set
{
pauseOnLastFrame = value;
MediaEndedHandler = value
? new PauseOnLastFrameHandler()
: new NullMediaEndedHandler();
}
}
public IMediaEndedHandler MediaEndedHandler = new NullMediaEndedHandler();
}
Then:
private void OnMediaEndedCommand()
{
if (GeneralSettings.MediaEndedHandler.AlternateHandling(MediaPlayer))
return;
if (PlayListViewModel.FilesCollection.Last().Equals(PlayListViewModel.FilesCollection.Current) && !Repeat)
{
ChangeMediaPlayerSource(PlayListViewModel.ChangeCurrent(() => PlayListViewModel.FilesCollection.MoveNext()));
MediaPlayer.SetMediaState(MediaPlayerStates.Stop);
return;
}
ChangeMediaPlayerSource(PlayListViewModel.ChangeCurrent(() => PlayListViewModel.FilesCollection.MoveNext()));
}
This way, if your setting is, for example. an enum instead of a bool, you can specify a different implementation of the interface for each possible value.

How can I use an IValueConverter to bind to different properties of an object in WPF?

I have a property in my view model which has a property of a class which has multiple properties e.g.
public class Content
{
public int Selector { get; set; }
public int Value1 { get; set; }
public int Value2 { get; set; }
public int Value3 { get; set; }
public int Value4 { get; set; }
}
public class ViewModel
{
public Content ContentInstance { get; set; }
}
and I want to bind it in my xaml with a converter such that the value of the selector determines which value is bound to the element e.g.
<TextBox Text="{Binding ContentInstance, Converter="ContentValueConverter", TargetNullValue='', Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
So far I have:
public class ContentValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var contentPart = value as Content;
if(contentPart == null) return;
switch(contentPart.Selector)
{
case 1:
return contentPart.Value1;
//etc
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This works to display the value but does not save the value back to the model.
I would prefer to keep this in a IValueConverter as this has to be added to many places in the codebase. Any help to get the value saving back to the model would be appreciated.
Your approach has one more flaw - any changes made to any of Content's properties will not be picked up by WPF, even if Content implements INotifyPropertyChanged.
If you don't care then, theoretically, depending on your scenario, you could store reference to the Content object that gets passed to Convert method and reuse it in ConvertBack. It's not very clean nor WPFish, requires a separate converter's instance per binding (so converter has to be defined inline, not as a resource).
So why don't you implement a proxy property in your ViewModel instead?
public class ViewModel
{
public Content ContentInstance { get; set; }
public int Value
{
get
{
switch (Content.Selector)
{
case 1:
return contentPart.Value1;
//etc
}
}
set
{
switch (Content.Selector)
{
case 1:
contentPart.Value1 = value;
break;
//etc
}
}
}
}
Then you can bind directly to it:
<TextBox Text="{Binding Value, Mode=TwoWay}"/>
Clean and effective. If your Content implements INotifyPropertyChanged then ViewModel can intercept it and raise changed events for Value property too.
Bind a textbox to each property and put them in the same grid column/row. Bind their visibility to the selector property with a converter that takes which value of the selector will make it visible. Now your visible textbox is bound to the right property and controlled by your selector selection. You can make it reusable by putting it in a separate UserControl or a ControlTemplate.
You need to implement also the ConvertBack method on ContentValueConverter.
ConvertBack is used to convert the result back to the view model
You can't do it like this because you need a ConvertBack method that returns a new Content instance.
I would create 5 text boxes, each binded two way on the field and hide them based on the Selector value (later edit: I see Lee O. gave the same solution but here is some code).
<TextBox Text="{Binding ContentInstance.Value1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ContentInstance.Selector, Converter=SelectorToVisibilityConverter, ConverterParameter=1}"/>
<TextBox Text="{Binding ContentInstance.Value2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding ContentInstance.Selector, Converter=SelectorToVisibilityConverter, ConverterParameter=2}"/>
...
and the converter:
public class SelectorToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var selector = (int)value;
var desired = (int)parameter; //may need a string to int conversion
return selector == desired ? Visibility.Visible : Visibility.Collapsed;
}
}
Note that this is written in notepad and it's not tested at all!
I guess you could also use some attached behavior.
Here is a solution based on an attached dependency property:
public static class GetValueBasedOnContentSelectorBehavior
{
private static readonly Dictionary<Content, TextBox> Map = new Dictionary<Content, TextBox>();
public static readonly DependencyProperty ContentProperty = DependencyProperty.RegisterAttached(
"Content", typeof(Content), typeof(GetValueBasedOnContentSelectorBehavior), new PropertyMetadata(default(Content), OnContentChanged));
private static void OnContentChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var textBox = dependencyObject as TextBox;
if (textBox == null) return;
var oldContent = dependencyPropertyChangedEventArgs.OldValue as Content;
if (oldContent != null && Map.Remove(oldContent))
oldContent.PropertyChanged -= ContentOnPropertyChanged;
var newContent = dependencyPropertyChangedEventArgs.NewValue as Content;
if (newContent != null)
{
newContent.PropertyChanged += ContentOnPropertyChanged;
Map.Add(newContent, textBox);
RedoBinding(textBox, newContent);
}
}
private static void ContentOnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
var content = sender as Content;
if (content == null) return;
TextBox textBox;
if (args.PropertyName == "Selector" && Map.TryGetValue(content, out textBox))
RedoBinding(textBox, content);
}
private static void RedoBinding(TextBox textBox, Content content)
{
textBox.SetBinding(TextBox.TextProperty,
new Binding { Source = content, Path = new PropertyPath("Value" + content.Selector) });
}
public static Content GetContent(TextBox txtBox)
{
return (Content)txtBox.GetValue(ContentProperty);
}
public static void SetContent(TextBox txtBox, Content value)
{
txtBox.SetValue(ContentProperty, value);
}
}
how it's used:
<TextBox testWpf:GetValueBasedOnContentSelectorBehavior.Content="{Binding ContentInstance}"/>
<ComboBox SelectedItem="{Binding ContentInstance.Selector}" ItemsSource="{Binding AvailableValues}"/>
and some other added things:
public class Content : INotifyPropertyChanged
{
private int _selector;
public int Selector
{
get { return _selector; }
set
{
_selector = value;
OnPropertyChanged();
}
}
public int Value1 { get; set; }
public int Value2 { get; set; }
public int Value3 { get; set; }
public int Value4 { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ViewModel
{
public Content ContentInstance { get; set; }
public IEnumerable<int> AvailableValues { get { return Enumerable.Range(1, 4); } }
}

Using controls in a LongListSelector

I'm using a LongListSelector to display a list of complex objects and update the datatemplate (a control) depending on the number of items in the bound objects list property.
I have tried the following.
Accessing the data in the OnItemRealized event to try and get the bound control and update it via a method call.
Not sure if possible
Adding a property to the control being bound which adds controls to a wrap panel when the property is set.
The set accessor in the controls property is never hit.
Hopefully it's clear what im trying to achieve.
Is it possible to call functionality in a property being bound to as shown in my control
If not is it possible to access the control being bound and call exposed functionality that way
If anyone could shed any light on my issue i would greatly appreciate it!
Data Template
<DataTemplate x:Key="LLS_SomeTemplate" >
<MyApp:ObjectTemplate SomeObjects="{Binding SomeEntities}"/>
</DataTemplate>
Object being bound
public class SomeObject
{
public ObservableCollection<Entities> _SomeEntities { get; set; }
public ObservableCollection<Entities> SomeEntities
{
get
{
if (_SomeEntities == null)
_SomeEntities = new ObservableCollection<Entities>();
return _SomeEntities;
}
set
{
_SomeEntities = value;
}
}
public SomeObject()
{
}
}
Control Property
public static DependencyProperty SomeObjectsProperty = DependencyProperty.Register("SomeObjects", typeof(ObservableCollection<Entities>), typeof(ObjectTemplate), new PropertyMetadata(new ObservableCollection<Entities>()));
public ObservableCollection<SomeObject> SomeObjects
{
get
{
return (ObservableCollection<SomeObject>)GetValue(SomeObjectsProperty);
}
set
{
SetValue(SomeObjectsProperty, value);
if (value != null && value.Count > 0)
{
foreach (SomeObject eLink in value)
{
//Add a new control to a wrap panel for each object in the list
}
}
}
}
There are few ways how CLR set up dependency properties. You must avoid perform operations in setter. Create value changed event handler instead:
public static DependencyProperty SomeObjectsProperty = DependencyProperty.Register("SomeObjects", typeof(ObservableCollection<Entities>), typeof(ObjectTemplate), new PropertyMetadata(new ObservableCollection<Entities>(), new PropertyChangedCallback(OnSomeObjectsPropertyChanged));
private static void OnSomeObjectsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as ObjectTemplate).UpdateSomeObjects(e.NewValue as SomeObjects);
}
public void UpdateSomeObjects(SomeObjects value)
{
if (value != null && value.Count > 0)
{
foreach (SomeObject eLink in value)
{
//Add a new control to a wrap panel for each object in the list
}
}
}
Hope it helps you to solve your problem

Changing standard property into a DependencyProperty

In developing some UserControls for internal use I followed this exmaple from MSDN http://msdn.microsoft.com/en-us/library/vstudio/ee712573(v=vs.100).aspx
The public value of one control is used by another control. The way I have this working currently is hooking into an event that is fired in the first control through code-behind. I am thinking that making one or both of the properties DependencyProperties which would eliminate the need for the code-behind.
public partial class UserControl1 : UserControl
{
private DataModel1 dm;
public UserControl1()
{
this.DataContext = new DataModel1();
dm = (DataModel1)DataContext;
InitializeComponent();
}
public DataValue CurrentValue
{
get { return dm.CurrentValue; }
set { dm.CurrentValue = value; }
}
}
public class DataModel1 : INotifyPropertyChanged
{
private DataValue _myData = new DataValue();
public DataValue CurrentValue
{
get { return _myData; }
set { if (_myData != value) {_myData = value OnPropertyChanged("CurrentValue"); }
}
// INotifyPropertyChanged Section....
}
The property is just a pass through from the DataModel1 class.
Both UserControls are very similar in their structure and have the same public properties. I would like to replace the code behind eventhandler with a Binding similar, I think to:
<my:UserControl1 Name="UserControl1" />
<my:UserControl2 CurrentValue={Binding ElementName="UserControl1", Path="CurrentValue"} />
but the standard examples of DependencyProperties have getters and setter that use the GetValue and SetValue functions which use a generated backing object instead of allowing a pass through.
public DataValue CurrentValue
{
get { return (DataValue)GetValue(CurrentValueProperty); }
set { SetValue(CurrentValueProperty, value); }
}
I think the DP should look like:
public static readonly DependencyProperty CurrentValueProperty =
DependencyProperty.Register("CurrentValue", typeof(DataValue), typeof(UserControl1));
How can I change the definition of the public backing property to support the databinding pass through?
I found that jumping into the OnPropertyChanged event allowed me to pass the data through to the DataModel1. I am not 100% sure that this is the correct answer but it gets the job done.
Here is the corrected code:
public static readonly DependencyProperty CurrentValueProperty =
DependencyProperty.Register("CurrentValue", typeof(DataValue), typeof(UserControl1),
new PropertyMetadata(new PropertyChangedCallback(OnCurrenValueChanged)));
private static void OnCurrentValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UserControl1 uc = d as UserControl1;
if (e.NewValue != null)
{
uc.dm.CurrentValue = e.NewValue as DataValue;
}
}
public DataValue CurrentValue
{
get { return GetValue(CurrentValueProperty) as DataValue; }
set { SetValue(CurrentValueProperty, value); }
}

c# - How to set default values of custom controls for wpf designer?

How do I set default values for my custom wpf components?
I have a Textfield with the property "public Protection Protection{set;get;}". Protection is an enum:
public class Field3270Attributes{
public enum Protection
{
PROTECTED,
UNPROTECTED,
AUTOSKIP
}
}
The default value should be autoskip but protected is listed as the default value in the wpf designer since its the first element in the enum.
Setting the protection in the Textfield constructor did not help.
I've tried DependencyProperty which does work but I have to specify a callback(setProtection) if I want any values except the default value to work.
If I dont specify a callback, changing values inside the wpf designer has no effect.
Is there a way to get the same behavior without having to specify callback methods for every property?
public class Textfield{
public static readonly DependencyProperty ProtectionProperty =
DependencyProperty.Register("Protection",
typeof(Field3270Attributes.Protection),
typeof(Textfield3270),
new FrameworkPropertyMetadata(Field3270Attributes.Protection.PROTECTED, setProtection));
private static void setProtection(object sender, DependencyPropertyChangedEventArgs e)
{
Textfield field = (Textfield)sender;
field.Protection = (Field3270Attributes.Protection)e.NewValue;
}
private Field3270Attributes.Protection protection;
public Field3270Attributes.Protection Protection
{
get
{
return protection;
}
set
{
this.protection = value;
if (value == Field3270Attributes.Protection.UNPROTECTED)
{
this.IsReadOnly = false;
Background = Brushes.White;
}
else
{
this.IsReadOnly = true;
Background = Brushes.LightSteelBlue;
}
}
}
public Textfield3270()
{
this.Protection = Field3270Attributes.Protection.PROTECTED;
}
}
Your DependencyProperty definition defines the default value. Change the first parameter in the FrameworkPropertyMetadata from PROTECTED to AUTOSKIP
public static readonly DependencyProperty ProtectionProperty =
DependencyProperty.Register("Protection",
typeof(Field3270Attributes.Protection),
typeof(Textfield3270),
new FrameworkPropertyMetadata(Field3270Attributes.Protection.AUTOSKIP, setProtection));
EDIT
You are overwriting your Protection DependencyProperty by implementing your own version with the same name. Remove your definition of the Protection {get; set;} completely.
If you want them to show up in the XAML designer, define the Get/Set as static methods like so:
public static Field3270Attributes.Protection GetProtection(DependencyObject obj)
{
return (Field3270Attributes.Protection)obj.GetValue(ProtectionProperty);
}
public static void SetProtection(DependencyObject obj, Field3270Attributes.Protection value)
{
obj.SetValue(ProtectionProperty, value);
}
If you want to attach some logic on PropertyChanged, you can use this code in your class constructor:
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(Textfield.ProtectionProperty, typeof(Textfield));
if (dpd != null) dpd.AddValueChanged(this, delegate { Protection_Changed(); });
And your changed method would look like this:
private void Protection_Changed()
{
Field3270Attributes.Protection protection = GetProtection(this);
// Do something with value
}

Categories

Resources