I'm creating a WPF program and I have created a custom Usercontrol and custom Textbox
When I rebuild my solution in visual studio i get this error.
Cannot set Name attribute value 'SearchT' on element 'HintTextBox'. 'HintTextBox' is under the scope of element 'ClickableControl', which already had a name registered when it was defined in another scope
I don't know what I need to do. Or what I did wrong? can someone help me? The classes below are the usercontrol and the hinttextbox, the last one is how I implmented them in xaml.
This is how I put the textbox in my Usercontrol
TEXTBOX = HintTextBox:
namespace View.custom_usercontrols
{
public partial class HintTextBox : TextBox
{
public static readonly DependencyProperty HintepDependencyProperty = DependencyProperty.Register("Hint", typeof(string), typeof(HintTextBox));
public string Hint
{
get
{
return (string)GetValue(HintepDependencyProperty);
}
set
{
SetValue(HintepDependencyProperty, value);
}
}
private string _text;
private bool _placeHolder;
public HintTextBox()
{
InitializeComponent();
if (Hint == null)
{
_text = "";
}
else
{
_text = Hint;
}
_placeHolder = true;
Text = _text;
Opacity = 0.2;
}
//extra code
}
}
This is my UserControl = ClickableControl
namespace View.custom_usercontrols
{
[ContentProperty(nameof(Children))]
public partial class ClickableControl : UserControl
{
public static readonly DependencyPropertyKey ChildrenProperty = DependencyProperty.RegisterReadOnly(
nameof(Children), // Prior to C# 6.0, replace nameof(Children) with "Children"
typeof(UIElementCollection),
typeof(ClickableControl),
new PropertyMetadata());
public static readonly DependencyProperty HoverColorDependencyProperty = DependencyProperty.Register("HoverColor", typeof(Brush), typeof(HintTextBox));
public static readonly DependencyProperty SelectedColorDependencyProperty = DependencyProperty.Register("SelectedColor", typeof(Brush), typeof(HintTextBox));
public static readonly DependencyProperty SelectedDependencyProperty = DependencyProperty.Register("Selected", typeof(Boolean), typeof(HintTextBox));
public Brush HoverColor
{
get
{
return (Brush)GetValue(HoverColorDependencyProperty);
}
set
{
SetValue(HoverColorDependencyProperty, value);
}
}
public Brush SelectedColor
{
get
{
return (Brush)GetValue(SelectedColorDependencyProperty);
}
set
{
SetValue(SelectedColorDependencyProperty, value);
}
}
private Brush BackgroundColor { get; set; }
public Boolean Selected
{
get
{
return (Boolean)GetValue(SelectedDependencyProperty);
}
set
{
SetValue(SelectedDependencyProperty, value);
if (value)
{
Background = SelectedColor;
}
else
{
Background = BackgroundColor;
}
}
}
public UIElementCollection Children
{
get { return (UIElementCollection) GetValue(ChildrenProperty.DependencyProperty); }
private set { SetValue(ChildrenProperty, value); }
}
public ClickableControl()
{
InitializeComponent();
Children = Grid.Children;
}
//EXTRA CODE
}
}
XAML:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:View"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:customUsercontrols="clr-namespace:View.custom_usercontrols"
//somewhere in the layout
<customUsercontrols:ClickableControl MouseDown="Search_OnMouseDown"
GotFocus="Search_OnGotFocus"
Background="#444444">
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon Kind="Magnify"
Margin="25 0 0 0"
Height="25"
Width="25"
Foreground="White"
VerticalAlignment="Center"/>
<customUsercontrols:HintTextBox x:Name="SearchT"
Padding="15"
Hint="SEARCH"
Width="204">
</customUsercontrols:HintTextBox>
</StackPanel>
</customUsercontrols:ClickableControl>
Thank you verry mutch
This is a bit late, but for anyone that views this question and still wonder about it, here goes:
Don't inherit from UserControl(Which inherits from contentControl) and then change default Content property of it, and expect it's content to be recognized upon call to InitializeComponent();
The elements "inside" the UserControl are its Content. if you defer its content to another property, stuff will go haywire.
Either you put the control you want to name under the UserControl xaml definition(the usual way), or you add it in code behind and name it,
or you can create a custom control and set its ControlTemplate with the control you want and specify it as a PART of the control:
http://paulstovell.com/blog/wpf-part-names
Related
I am creating a login page in UWP using MVVM. When an incorrect password is put in I want the border of the PasswordBox to be changed to red to indicate it is incorrect.
I bound a SolidColorBrush variable to the Border and Background.
private SolidColorBrush _validationColorBorder;
public SolidColorBrush ValidationColorBorder
{
get{ return _validationColorBorder; }
set
{
_validationColorBorder = value;
RaisePropertyChanged();
}
}
private SolidColorBrush _validationColorBackground;
public SolidColorBrush ValidationColorBackground
{
get { return _validationColorBackground; }
set
{
_validationColorBackground = value;
RaisePropertyChanged();
}
}
in the ViewModel I set the colors to the validation Colors using this:
ValidationColorBackground = (SolidColorBrush)Application.Current.Resources["TextBoxBackgroundThemeBrush"];
ValidationColorBorder = (SolidColorBrush)Application.Current.Resources["TextBoxBorderThemeBrush"];
My issue is that after I set the password box background and border to these colors, I want to be able to set them back to the default colors afterwards.
How would I go about setting the colors back to the default colors of my application? And be able to do this in the MVVM format by setting my SolidColorBrush variables to these default colors?
Thanks for your help!
How to reset Background color to default in MVVM?
Please check PasswrodBox style. The default Background and Border color are TextControlBackground and TextControlBorderBrush.
So you could get them before you set the custom color.
defaultBgColor = (SolidColorBrush)Application.Current.Resources["TextControlBackground"];
defaultBorderColor = (SolidColorBrush)Application.Current.Resources["TextControlBorderBrush"];
If you want to rollback,you just need to copy above value to your custom binding property. For more please refer the following.
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private SolidColorBrush defaultBgColor;
private SolidColorBrush defaultBorderColor;
public ViewModel()
{
defaultBgColor = (SolidColorBrush)Application.Current.Resources["TextControlBackground"];
defaultBorderColor = (SolidColorBrush)Application.Current.Resources["TextControlBorderBrush"];
ValidationColorBackground = (SolidColorBrush)Application.Current.Resources["TextBoxBackgroundThemeBrush"];
ValidationColorBorder = (SolidColorBrush)Application.Current.Resources["TextBoxBorderThemeBrush"];
}
private SolidColorBrush _validationColorBorder;
public SolidColorBrush ValidationColorBorder
{
get { return _validationColorBorder; }
set
{
_validationColorBorder = value;
RaisePropertyChanged();
}
}
private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private SolidColorBrush _validationColorBackground;
public SolidColorBrush ValidationColorBackground
{
get { return _validationColorBackground; }
set
{
_validationColorBackground = value;
RaisePropertyChanged();
}
}
public ICommand BtnClickCommand
{
get
{
return new RelayCommand(() =>
{
ValidationColorBackground = defaultBgColor;
ValidationColorBorder = defaultBorderColor;
});
}
}
}
you could use Bevaviours to accomlpish this. You would need a property for Errors to bind to.
You would also need to create some Styles
See an example (=> taken from Prism)
using Microsoft.Xaml.Interactivity;
using Prism.Windows.Validation;
using System.Collections.ObjectModel;
using System.Linq;
using Windows.UI.Xaml;
namespace ams.TaskmanagerClient.Behaviors
{
Public Class HighlightFormFieldOnErrors : Behavior<FrameworkElement>
{
Public ReadOnlyCollection<String> PropertyErrors
{
Get { return (ReadOnlyCollection<String>)GetValue(PropertyErrorsProperty); }
Set { SetValue(PropertyErrorsProperty, value); }
}
Public String HighlightStyleName
{
Get { return (String)GetValue(HighlightStyleNameProperty); }
Set { SetValue(HighlightStyleNameProperty, value); }
}
Public String OriginalStyleName
{
Get { return (String)GetValue(OriginalStyleNameProperty); }
Set { SetValue(OriginalStyleNameProperty, value); }
}
Public static DependencyProperty PropertyErrorsProperty =
DependencyProperty.RegisterAttached("PropertyErrors", typeof(ReadOnlyCollection<String>), typeof(HighlightFormFieldOnErrors), New r opertyMetadata(BindableValidator.EmptyErrorsCollection, OnPropertyErrorsChanged));
// The default For this Property only applies To TextBox controls.
Public static DependencyProperty HighlightStyleNameProperty =
DependencyProperty.RegisterAttached("HighlightStyleName", typeof(String), typeof(HighlightFormFieldOnErrors), New r op ert yMetadata("HighlightTextBoxStyle"));
// The default For this Property only applies To TextBox controls.
protected static DependencyProperty OriginalStyleNameProperty =
DependencyProperty.RegisterAttached("OriginalStyleName", typeof(Style), typeof(HighlightFormFieldOnErrors), New r op ert yMetadata("BaseTextBoxStyle"));
Private static void OnPropertyErrorsChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
If (args == Null || args.NewValue == Null)
{
return;
}
var control = ((Behavior<FrameworkElement>)d).AssociatedObject;
var propertyErrors = (ReadOnlyCollection<String>)args.NewValue;
Style style = (propertyErrors.Any()) ?
(Style)Application.Current.Resources[((HighlightFormFieldOnErrors)d).HighlightStyleName] :
(Style)Application.Current.Resources[((HighlightFormFieldOnErrors)d).OriginalStyleName];
control.Style = style;
}
protected override void OnAttached()
{
base.OnAttached();
}
protected override void OnDetaching()
{
base.OnDetaching();
}
}
}
<Page
x:Class="ams.TaskmanagerClient.Views.SettingsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:helper="using:ams.TaskmanagerClient.Helpers"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Style="{StaticResource PageStyle}"
xmlns:prismMvvm="using:Prism.Windows.Mvvm"
xmlns:vm="using:ams.TaskmanagerClient.ViewModels"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:ic="using:Microsoft.Xaml.Interactions.Core"
xmlns:helpers="using:ams.TaskmanagerClient.Helpers"
xmlns:behaviors="using:ams.TaskmanagerClient.Behaviors"
prismMvvm:ViewModelLocator.AutoWireViewModel="True"
xmlns:xaml="using:Windows.UI.Xaml"
mc:Ignorable="d">
<Page.Resources>
<helper:EnumToBooleanConverter x:Key="EnumToBooleanConverter" EnumType="ElementTheme" />
</Page.Resources>
<Grid>
<PasswordBox x:Name="ClientSecretPasswordBox"
x:Uid="ClientSecretPasswordBox"
AutomationProperties.AutomationId="ClientSecretPasswordBox"
AutomationProperties.IsRequiredForForm="True"
Password="{Binding NewClient.ClientSecret, Mode=TwoWay}"
IsTabStop="True">
<i:Interaction.Behaviors>
<behaviors:HighlightFormFieldOnErrors OriginalStyleName="BasePasswordBoxStyle"
HighlightStyleName="HighlightPasswordBoxStyle"
PropertyErrors="{Binding NewClient.Errors[ClientSecret], Mode=OneWay}" />
</i:Interaction.Behaviors>
</PasswordBox>
</Grid>
</Page>
This question already has answers here:
Issue with DependencyProperty binding
(3 answers)
Closed 3 years ago.
I am trying to create a simple UserControl in WPF for reusing in my other applications. It is a simple DateRangePicker. Some of the control's properties are bound to child UI elements and hence I implement INotifyPropertyChanged. My control.xaml.cs looks like below (only relevant portions)
public partial class DateRangePicker : UserControl, INotifyPropertyChanged
{
public DateRangePicker()
{
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
if (PropertyChanged != null)
{
var e = new PropertyChangedEventArgs(propertyName);
PropertyChanged(this, e);
}
}
public static readonly DependencyProperty InitialFromDateProperty =
DependencyProperty.Register("InitialFromDate", typeof(DateTime), typeof(DateRangePicker), new PropertyMetadata(default(DateTime),
new PropertyChangedCallback(OnInitialDateRangeChanged)));
public static readonly DependencyProperty InitialToDateProperty =
DependencyProperty.Register("InitialToDate", typeof(DateTime), typeof(DateRangePicker), new PropertyMetadata(default(DateTime),
new PropertyChangedCallback(OnInitialDateRangeChanged)));
public static readonly DependencyProperty SelectedFromDateProperty =
DependencyProperty.Register("SelectedFromDate", typeof(DateTime), typeof(DateRangePicker), new PropertyMetadata(default(DateTime), null));
public static readonly DependencyProperty SelectedToDateProperty =
DependencyProperty.Register("SelectedToDate", typeof(DateTime), typeof(DateRangePicker), new PropertyMetadata(default(DateTime), null));
public DateTime InitialFromDate
{
get { return (DateTime)GetValue(InitialFromDateProperty); }
set
{
SetValue(InitialFromDateProperty, value);
}
}
public DateTime InitialToDate
{
get { return (DateTime)GetValue(InitialToDateProperty); }
set
{
SetValue(InitialToDateProperty, value);
}
}
public DateTime SelectedFromDate
{
get { return (DateTime)GetValue(SelectedFromDateProperty); }
set
{
SetValue(SelectedFromDateProperty, value);
}
}
public DateTime SelectedToDate
{
get { return (DateTime)GetValue(SelectedToDateProperty); }
set
{
SetValue(SelectedToDateProperty, value);
}
}
private static void OnInitialDateRangeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DateRangePicker control = (DateRangePicker)d;
control.RefreshLists();
}
My Test application xaml file which hosts the control looks like below (some lines including namespaces have been removed):
<Window x:Class="WpfApp3.MainWindow"
xmlns:ControlDateRangePicker ="clr-namespace:SurfServer.Apps.Common.UI"
xmlns:local="clr-namespace:WpfApp3"
Title="MainWindow" Height="414.831" Width="565.808">
<Grid>
<ControlDateRangePicker:DateRangePicker
InitialFromDate="{Binding InitialFromDateVM, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
InitialToDate="{Binding InitialToDateVM, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedFromDate ="{Binding SelectedFromDateVM, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedToDate ="{Binding SelectedToDateVM, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
HorizontalAlignment="Left" Margin="136,78,0,0" VerticalAlignment="Top" Width="347"/>
</Grid>
</Window>
And my Test Application xaml.cs looks like this
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
try
{
var viewModel = new SampleViewModel();
DataContext = viewModel;
viewModel.Initialize();
}
catch (Exception)
{
Close();
}
}
}
My Test Application's ViewModel looks like below :
class SampleViewModel : ViewModelBase
{
private DateTime m_dtInitialFrom;
public DateTime InitialFromDateVM
{
get => m_dtInitialFrom;
set
{
m_dtInitialFrom = value;
RaisePropertyChangedEvent(nameof(InitialFromDateVM));
}
}
private DateTime m_dtInitialTo;
public DateTime InitialToDateVM
{
get => m_dtInitialTo;
set
{
m_dtInitialTo = value;
RaisePropertyChangedEvent(nameof(InitialToDateVM));
}
}
public DateTime SelectedFromDateVM
{
get;
set;
}
public DateTime SelectedToDateVM
{
get;
set;
}
public void Initialize()
{
InitialFromDateVM = new DateTime(DateTime.Now.Year - 1, DateTime.Now.Month, DateTime.Now.Day);
InitialToDateVM = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day);
}
As you can see, some of the properties in the test application's View model are bound to my control's (DateRangePicker) dependency properites.
Now the problem I am facing is, though I am trying to set the Initial values in my Test Application's ViewModel (in the Initialize method), it looks like the binding does not work and I am not getting a callback in my control (in fact, I am unable to hit even the 'set' of the dependency property itself). What am I trying to do wrong here ?
You have to remove setting DataContext from DateRangePicker constructor. So the DataContext for your UserControl was not your ViewModel SampleViewModel but the UserControl itself.
public partial class DateRangePicker : UserControl, INotifyPropertyChanged
{
public DateRangePicker()
{
InitializeComponent();
//DataContext = this; REMOVE
}
}
I'm using the WPF Extended Toolkit Property Grid. I'm using an editor user control to display a list of objects, it looks like this:
My user wants to be able to change the number in the "Elements" description, and have the code adjust the number of elements in the list.
{ removed incomplete code, see answer below for working code }
Is there some way to put my own control to replace the label that says "3 elements" with a TextBox so I can process changes to the text and change my arrays?
Thanks,
David
Ok, sorry for the delay, but here is the working code for my question...I hope it helps someone.
As a recap, what I wanted to do, was produce a PropertyGrid Entry that looks like this:
In summary, what you need to do is create two User Control property Grid Editors, one for the summary line (the spin box and element label above), and another for the data list. All of the associated code is below:
So, to start, here is the Element class:
public class Element
{
public Element(int number, double wtf)
{
Number = number;
WTF = wtf;
}
public int Number { get; set; }
public double WTF { get; set; }
}
I also have a View Model for the Element:
public class ElementViewModel : XTRRABase
{
public Element _element;
public ElementViewModel(Element element)
{
_element = element;
}
public int Number
{
get { return _element.Number; }
set { _element.Number = value; NotifyPropertyChanged(); }
}
public double WTF
{
get { return _element.WTF; }
set { _element.WTF = value; NotifyPropertyChanged(); }
}
public String ElementInfo
{
get { return XTRRAApp.Application.AtomicElementList.GetElements()[Number]; }
set { }
}
}
The ElementInfo property returns the element name (like "6 (Carbon)" in the example).
In the parent view model (the object containing the Elements property), the property looks like this:
ElementListViewModel _elements;
[PropertyOrder(4), DisplayName("Elements")]
[ExpandableObject]
[Editor(typeof(ElementHeaderUCEditor), typeof(ElementHeaderUCEditor))]
public ElementListViewModel Elements
{
get { return (_elements = new ElementListViewModel(_material.Elements) ); }
set {}
}
Note that this object is both ExpandableObject and has a defined editor ElementHeaderUCEditor
The ElementHeaderUCEditor defines the IntegerUpDown spin box and the 'elements' label. It's XAML looks like this:
<UserControl x:Class="XTRRAApp.View.Editors.ElementHeaderUCEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit">
<StackPanel Orientation="Horizontal" Background="White">
<xctk:IntegerUpDown Text="{Binding Value.Count,UpdateSourceTrigger=PropertyChanged}" Width="100" Margin="2,2,2,2" ParsingNumberStyle="Integer"/>
<Label Content="Elements" Width="Auto" Margin="2,2,2,2"/>
</StackPanel>
</UserControl>
and the code-behind:
public partial class ElementHeaderUCEditor : UserControl, ITypeEditor
{
public ElementHeaderUCEditor()
{
InitializeComponent();
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(ElementListViewModel), typeof(ElementHeaderUCEditor),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public ElementListViewModel Value
{
get { return (ElementListViewModel)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public FrameworkElement ResolveEditor(Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
{
Binding binding = new Binding("Value");
binding.Source = propertyItem;
binding.Mode = propertyItem.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay;
BindingOperations.SetBinding(this, ElementHeaderUCEditor.ValueProperty, binding);
return this;
}
}
Next, here is the ElementListViewModel which provides the data for the List:
[DisplayName("Elements")]
public class ElementListViewModel : XTRRABase
{
protected List<Element> _elements;
public ElementListViewModel(List<Element> elements)
{
_elements = elements;
}
[Browsable(false)]
public int Count
{
get { return _elements.Count; }
set
{
while(value < _elements.Count)
{
_elements.RemoveAt(_elements.Count - 1);
}
while(value > _elements.Count)
{
_elements.Add(new Element(0,0));
}
NotifyPropertyChanged();
NotifyPropertyChanged("Elements");
}
}
[PropertyOrder(1), DisplayName("Elements")]
[Editor(typeof(ElementUCEditor), typeof(ElementUCEditor))]
public ObservableCollection<ElementViewModel> Elements
{
get
{
ObservableCollection<ElementViewModel> list = new ObservableCollection<ElementViewModel>();
foreach(Element element in _elements)
{
list.Add(new ElementViewModel(element));
}
return list;
}
set { }
}
}
XTRRABase is just a common base class I use to avoid duplicating the notification code:
public abstract class XTRRABase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The Elements, as you can see are displayed using another User Control `ElementUCEditor' it's XAML looks like this:
and the code-behind for it:
public partial class ElementUCEditor : UserControl, ITypeEditor
{
public ElementUCEditor()
{
InitializeComponent();
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(ElementListViewModel), typeof(ElementUCEditor),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public ElementListViewModel Value
{
get { return (ElementListViewModel)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public FrameworkElement ResolveEditor(Xceed.Wpf.Toolkit.PropertyGrid.PropertyItem propertyItem)
{
Binding binding = new Binding("Value");
binding.Source = propertyItem;
binding.Mode = propertyItem.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay;
BindingOperations.SetBinding(this, ElementUCEditor.ValueProperty, binding);
return this;
}
}
I used a propertyGrid as CustomEditor:
public partial class PropertyGridEditor : ITypeEditor
{
public PropertyGridEditor()
{
InitializeComponent();
}
public FrameworkElement ResolveEditor(PropertyItem propertyItem)
{
if (propertyItem.Value != null)
{
var objects = propertyItem.Value;
foreach (var o in (IEnumerable)objects)
{
var propertyGrid = new Xceed.Wpf.Toolkit.PropertyGrid.PropertyGrid
{
IsCategorized = false,
IsMiscCategoryLabelHidden = true,
ShowAdvancedOptions = false,
ShowDescriptionByTooltip = true,
ShowPreview = false,
ShowSearchBox = false,
ShowSortOptions = false,
ShowTitle = true,
ShowSummary = false,
SelectedObject = o,
};
Container.Children.Add(propertyGrid);
}
}
return this;
}
}
XAML
<UserControl x:Class="ibKastl.Helper.UserControls.PropertyGrid.Editor.PropertyGridEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Orientation="Vertical" Name="Container">
</StackPanel>
</UserControl>
So you can add any Enumerable to a Property grid
I am using WPF (C#) for the first time and this I've encountered my first "real" design choice. I have a main window and when the user enters some data and presses the "plot" Button, a new window will come up showing a graph.
This graph window I am defining myself with a combination of xaml and the code-behind file. The issue is that 2 parameters this window has is the x axis title and the y axis title. So, these should be "parameters" to making this window.
I am confused by this because I'm using MVVM and I have a "ViewModel" for the window called GraphWindowPresenter and a "View" for the class called GraphWindowView.
At first, I tried to have an xAxis property and a yAxis property in my GraphWindowPresenter but that will not work since I need to "bind" to these values upon construction of the GraphWindowView. Additionally, this approach would require that my GraphWindowPresenter take an xAxis parameter and a yAxis parameter which is problamatic as well since I just create an instance of the class in the xaml of GraphWindowView.
I'm thinking of a possible soltuion that I can just have my GraphWindowView take the xAxis and yAxis parameters but doesn't this violate MVVM? I would rather not do that.
Note: This is similar to this post MVVM: Binding a ViewModel which takes constructor args to a UserControl. But in my scenario it is tricky since I have a parent window and a pop up child window.
Question: What is the best approach to this design issue? What are the "best practices" regarding this scenario?
Possible Answer:
Is this the correct use of dependency properties that you described? Is this a "clean" solution?
private void doGraph()
{
if (log == null) // if a log is not loaded
{
MessageBoxResult mbr = MessageBox.Show("A log file must be " +
"loaded before plotting.",
"Warning",
MessageBoxButton.OK,
MessageBoxImage.Exclamation);
return;
}
// NOW MUST PRESENT GRAPH WINDOW
GraphWindowView gwv = new GraphWindowView();
gwv.xAxis = X_AXIS_VALUE:
gwv.yAxis = Y_AXIS_VALUE;
gwv.Show();
}
And in my GraphWindowView class I have the code:
public partial class GraphWindowView : Window
{
// Using a DependencyProperty as the backing store for yAxis.
public static readonly DependencyProperty yAxisProperty =
DependencyProperty.Register("yAxis", typeof(string), typeof(GraphWindowView));
// Using a DependencyProperty as the backing store for xAxis.
public static readonly DependencyProperty xAxisProperty =
DependencyProperty.Register("xAxis", typeof(string), typeof(GraphWindowView));
public string xAxis
{
get { return (string)GetValue(xAxisProperty); }
set { SetValue(xAxisProperty, value); }
}
public string yAxis
{
get { return (string)GetValue(yAxisProperty); }
set { SetValue(yAxisProperty, value); }
}
public GraphWindowView()
{
InitializeComponent();
}
}
You can you userSetting properties
One my application have same scenario in that i have mainWindow that accept HostAddress,Port value and it will use another window when i click connect so i am using userSetting properties. I am also using MVVM pattern check code snippet below
XAML:
<TextBox Width="120" Canvas.Left="132" Canvas.Top="16" Text="{Binding Path=Server,Mode=TwoWay}"/>
<TextBox Width="120" Canvas.Left="132" Canvas.Top="42" Text="{Binding Path=DisplayPort,Mode=TwoWay}"/>
<TextBox Width="120" Canvas.Left="132" Canvas.Top="69" Text="{Binding Path=CtrlPort,Mode=TwoWay}"/>
<Button Content="Launch" Name="btnLaunch" Command="{Binding Path=appSetting}" Canvas.Left="132" Canvas.Top="100" Width="120" Height="51" Click="btnLaunch_Click" />
VIEWMODE:
public class SettingsViewModel : ViewModelBase
{
private Settings _settings { get; set; }
public SettingsViewModel()
{
appSetting = new RelayCommand(this.AppSettingsCommand);
_settings = ApplicationTest.Properties.Settings.Default;
}
private string _server = Settings.Default.Server;
public string Server
{
get { return this._server; }
set
{
if (this._server != value)
{
this._server = value;
OnPropertyChanged("Server");
}
}
}
private string _displayPort = Settings.Default.DisplayPort;
public string DisplayPort
{
get { return this._displayPort; }
set
{
if (this._displayPort != value)
{
this._displayPort = value;
OnPropertyChanged("DisplayPort");
}
}
}
private string _ctrlPort = Settings.Default.CtrlPort;
public string CtrlPort
{
get { return this._ctrlPort; }
set
{
if (this._ctrlPort != value)
{
this._ctrlPort = value;
OnPropertyChanged("DisplayPort");
}
}
}
public RelayCommand appSetting
{
get;
set;
}
private void AppSettingsCommand()
{
this._settings.Server = this.Server;
this._settings.DisplayPort = this.DisplayPort;
this._settings.CtrlPort = this.CtrlPort;
this._settings.Save();
}
I have a UserControl, we'll call it "Header". It has a DependencyProperty called ProjectID, this control has a View Model and I set it to be the DataContext:
public BillingInfoHeaderControlVM VM
{
get
{
return (BillingInfoHeaderControlVM)DataContext;
}
set
{
DataContext = value;
}
}
public static readonly DependencyProperty ProjectIDProperty =
DependencyProperty.Register("ProjectID", typeof(int), typeof(BillingInfoHeaderControl), new PropertyMetadata();
public int ProjectID
{
set
{
SetValue(ProjectIDProperty, value);
}
get
{
return (int)GetValue(ProjectIDProperty);
}
}
Now what I want to do, is to bind the ProjectID of a control to this control's ProjectID:
<controls:Header Grid.Row ="0" x:Name="Header" ProjectID="{Binding ProjectID, Mode=OneWay}"></controls:Header>
Now when I run this, I get an error in the InitializeControl() method that states "
Property Get method was not found.
From what I'm reading, I'm seeing this is because the Binding ProjectID is relative to the data context of the control. Of course I could set the ElementName within the binding:
<controls:Header Grid.Row ="0" x:Name="Header" ProjectID="{Binding ProjectID, Mode=OneWay, ElementName=ParentControl}"></controls:Header>
But this is ugly, and to be honest we don't want to have to remember to do this for this control whenever we use it. What other options do I have? Is there a way to set the source of the binding to use the DataContext of the parent?
I duplicated your concept in code and it compiles and runs fine.
I have included the control code and the viewmodel below in case you are doing something different.
*Note: I kept the viewmodel ProjectID as a simple update property.:
namespace Demo1
{
public partial class BillingInfoHeaderControl : UserControl
{
public BillingInfoHeaderControl()
{
InitializeComponent();
this.DataContext = new BillingInfoHeaderControlVM();
}
public int ProjectId
{
get { return (int)GetValue(ProjectIdProperty); }
set { SetValue(ProjectIdProperty, value); }
}
public static readonly DependencyProperty ProjectIdProperty =
DependencyProperty.Register("ProjectId", typeof(int), typeof(BillingInfoHeaderControl),
new PropertyMetadata(0));
}
}
namespace Demo1
{
public class BillingInfoHeaderControlVM : INotifyPropertyChanged
{
private int _projectId;
public int ProjectId
{
get { return _projectId; }
set
{
if (_projectId != value)
{
_projectId = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ProjectId"));
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}