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
Related
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 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
I am writing terimnal application in WPF. I receive characters from an embedded device and I update a TextBox.Text property that is bounded to my ViewModel.
The problem is that the TextBox Caret is reset when I update the text property. what I would like to do is hold a Caret parameter in my viewModel and bind it to the Caret property of the TextBox, however the TextBox Caret is not a dependency property and I don't want to access the view directly from my view model.
Are you familiar with a proper solutionthat does not break the MVVM pattern?
Thanks in advance.
You can add attached property to bind non-dependency property. below example i have created it for CaretIndex property of the textbox.
<Window x:Class="Converter_Learning.Window7"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Converter_Learning"
Title="Window7" Height="500" Width="500">
<Grid FocusManager.FocusedElement="{Binding ElementName=txt}">
<TextBox x:Name="txt" Text="Hiiiiiiiiiiiiiii" local:TextBoxHelper.Caret="{Binding Caret}" />
</Grid>
public partial class Window7 : Window
{
public Window7()
{
InitializeComponent();
this.DataContext= new CaretViewModel();
}
}
public class CaretViewModel : INotifyPropertyChanged
{
private int myVar;
public int Caret
{
get { return myVar; }
set { myVar = value; Notify("Caret"); }
}
public CaretViewModel()
{
Caret = 5;
}
public event PropertyChangedEventHandler PropertyChanged;
void Notify(string property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public static class TextBoxHelper
{
public static int GetCaret(DependencyObject obj)
{
return (int)obj.GetValue(CaretProperty);
}
public static void SetCaret(DependencyObject obj, int value)
{
obj.SetValue(CaretProperty, value);
}
public static readonly DependencyProperty CaretProperty =
DependencyProperty.RegisterAttached(
"Caret",
typeof(int),
typeof(TextBoxHelper),
new FrameworkPropertyMetadata(0, CaretChanged));
private static void CaretChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
TextBox tb = obj as TextBox;
if (tb != null)
{
int newValue = (int)e.NewValue;
if (newValue != tb.CaretIndex)
{
tb.CaretIndex = (int)newValue;
}
}
}
}
I'm learning WPF and, coming from Flex and AS, it seems overly complicated at times. Opinions aside, my problem is the following.
I've created a custom control, ToolBarButton which is basically an image button that is destined to be included in a custom toolbar. I've added some properties to this control and I'd like to be able to set them from the XAML. Though the property appears in AutoCompletion on the XAML side, the Set method is never fired and the property stays null. So here's the ToolBarButton Code Behind :
public static readonly DependencyProperty ImgSrcProperty = DependencyProperty.RegisterAttached("ImgSource", typeof(string), typeof(ToolBarButton));
public static readonly DependencyProperty OnClickProperty = DependencyProperty.Register("OnClick", typeof(RoutedEventHandler), typeof(ToolBarButton));
public ToolBarButton(RoutedEventHandler OnClick, string imgSrc, Map map = null, string ConfigFile = null) :
base(ConfigFile, map)
{
if (OnClick != null) SetValue(OnClickProperty, OnClick);
if (imgSrc != null) SetValue(ImgSrcProperty, imgSrc);
this.AddChild(CreateButton());
InitializeComponent();
}
public ToolBarButton() : this(null, null) { }
private Button CreateButton()
{
BitmapImage icon = new BitmapImage();
icon.BeginInit();
icon.UriSource = new Uri(ImgSource, UriKind.Relative);
icon.EndInit();
Image img = new Image();
img.Stretch = Stretch.Fill;
img.Source = icon;
Button BtnToAdd = new Button();
BtnToAdd.Width = 35;
BtnToAdd.Height = 35;
BtnToAdd.Content = img;
BtnToAdd.Background = new ImageBrush(icon);
BtnToAdd.Click += OnClick;
return BtnToAdd;
}
public string ImgSource
{
get { return (string)GetValue(ImgSrcProperty); }
set { SetValue(ImgSrcProperty, value); }
}
public RoutedEventHandler OnClick
{
get { return (RoutedEventHandler)GetValue(OnClickProperty); }
set { SetValue(OnClickProperty, value); }
}
You'll notice two constructors, one to create the control at runtime, the other to create it from XAML.
And here's the XAML code that uses the custom control but doesn't fire the set method :
<BaseControls:ToolBar
x:Class="Basic_Mapping.Widgets.NavigationToolBar"
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:BaseControls="clr-namespace:Basic_Mapping.Base_Controls"
mc:Ignorable="d" >
<BaseControls:ToolBarButton Width="35" Height="35" ImgSource="Assets/i_zoomin.png" ConfigFileName="ZoomIn.xml" />
Any help would be appreciated!
Ggilmann
EDIT :
Here's the Base Class used for the ToolBarButton, it also has the same problem with it's properties.
public partial class ConfigurableUserControl : UserControl
{
private XmlDocument configXML;
public static readonly DependencyProperty XmlProperty = DependencyProperty.Register("ConfigFileName", typeof(string), typeof(ConfigurableUserControl));
public static readonly DependencyProperty MapProperty = DependencyProperty.Register("Map", typeof(Map), typeof(ConfigurableUserControl));
public ConfigurableUserControl(string configFile, Map map)
{
if (configFile != null) SetValue(XmlProperty, configFile);
if (map != null) SetValue(MapProperty, map);
string file = (string)GetValue(XmlProperty);
if (file != null)
{
configXML = new XmlDocument();
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\\..\\Config\\" + configFile);
if (File.Exists(path)) configXML.Load(path);
}
}
public ConfigurableUserControl() : this(null, null) { }
public string ConfigFileName
{
//get { return (string)GetValue(XmlProperty); }
set { SetValue(XmlProperty, value); }
}
public Map Map
{
get { return (Map)GetValue(MapProperty); }
set { SetValue(MapProperty, value); }
}
public XmlDocument ConfigXML
{
get { return configXML; }
}
}
My guess is that this problem, and your problems with the base class, are due to the fact that you're not implementing INotifyPropertyChanged and making the appropriate calls.
Try putting InitializeComponent(); at the beginning of the constructor as opposed to at the end where it currently is.
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;
}
}