I have a property grid connected with public class properties.
As I have seen in many solutions by adding an EditorAttribute I should be able to use a file browser:
public class properties
{
public properties()
{
PartProgramConfigurationFilename = "Unknow";
}
[Category("File")]
// BELOW CUSTOM EDITOR
[EditorAttribute(typeof(System.Windows.Forms.FileDialog), typeof(System.Drawing.Design.UITypeEditor))]
[Description("Description"), DisplayName("PP configuration filename")]
public string PartProgramConfigurationFilename { get; set; }
}
So now what I expected is that when I click on the property grid a FileBroswer appears:
}
but nothing appears.
I have also followed this solution but again no result.
Unfortunately there is no custom editor out of the box, so I wrote one myself. Here is the code;
XAML:
<UserControl x:Class="MyControls.PropertyGridFilePicker"
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:Engine.Controls"
mc:Ignorable="d"
d:DesignHeight="20" d:DesignWidth="300"
x:Name="TheControl">
<DockPanel>
<Button x:Name="PickFileButton" Content="…" Click="PickFileButton_Click" DockPanel.Dock="Right" Width="15" />
<TextBox Text="{Binding ElementName=TheControl, Path=Value}" />
</DockPanel>
</UserControl>
Code Behind:
using Microsoft.Win32;
using System.Windows;
using System.Windows.Data;
using Xceed.Wpf.Toolkit.PropertyGrid;
using Xceed.Wpf.Toolkit.PropertyGrid.Editors;
namespace MyControls
{
/// <summary>
/// Interaction logic for PropertyGridFilePicker.xaml
/// </summary>
public partial class PropertyGridFilePicker : ITypeEditor
{
public PropertyGridFilePicker()
{
InitializeComponent();
}
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(PropertyGridFilePicker), new PropertyMetadata(null));
public FrameworkElement ResolveEditor(PropertyItem propertyItem)
{
Binding binding = new Binding("Value");
binding.Source = propertyItem;
binding.Mode = propertyItem.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay;
BindingOperations.SetBinding(this, ValueProperty, binding);
return this;
}
private void PickFileButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog fd = new OpenFileDialog();
if (fd.ShowDialog() == true && fd.CheckFileExists)
{
Value = fd.FileName;
}
}
}
}
And this is how you use it:
public class MySampleClass
{
[Editor(typeof(MyControls.PropertyGridFilePicker), typeof(MyControls.PropertyGridFilePicker))]
public string SomeDataModelString { get; set; }
}
Credit goes to Brian Lagunas for this tutorial.
Related
I am working on a reusable content dialog UserControl that the user can import into their xaml and only have to be responsible for bindings in their respective ViewModel. My UserControl has DependencyProperties tied to appropriate content, but the Content Dialog event's are not visible (programmatically) to users of the UserControl. Specifically, I am looking for .ShowAsync()., but Intellisense does not see .ShowAsync() as being part of UserControl.
I used the examples here as a pattern, but I don't need to extend the DialogContent class for my purpose (I think?): https://learn.microsoft.com/en-us/uwp/api/Windows.UI.Xaml.Controls.ContentDialog
As an experiment, I tried to make this SO work, but I then realized this was sitting on Template 10 framework and Dependency Injection/IoC (I am doing my app in straight C#): UWP ContentDialog Invocation
Here is my UserControl's code behind:
public partial class UserDefinedDialogView : UserControl
{
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("DialogTitle", typeof(string), typeof(UserDefinedDialogView), null);
public static readonly DependencyProperty DialogContentTextProperty =
DependencyProperty.Register("DialogContent", typeof(string), typeof(UserDefinedDialogView), null);
public static readonly DependencyProperty PrimaryButtonTextProperty =
DependencyProperty.Register("DialogPrimaryButtonText", typeof(string), typeof(UserDefinedDialogView), null);
public static readonly DependencyProperty SecondaryButtonTextProperty =
DependencyProperty.Register("DialogSecondaryButtonText", typeof(string), typeof(UserDefinedDialogView), null);
public static readonly DependencyProperty CloseButtonTextProperty =
DependencyProperty.Register("DialogCloseButtonText", typeof(string), typeof(UserDefinedDialogView), null);
// PropertyWrappers
public string DialogTitle
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public string DialogContent
{
get { return (string)GetValue(DialogContentTextProperty); }
set { SetValue(DialogContentTextProperty, value); }
}
public string DialogPrimaryButtonText
{
get { return (string)GetValue(PrimaryButtonTextProperty); }
set { SetValue(PrimaryButtonTextProperty, value); }
}
public string DialogSecondaryButtonText
{
get { return (string)GetValue(SecondaryButtonTextProperty); }
set { SetValue(SecondaryButtonTextProperty, value); }
}
public string DialogCloseButtonText
{
get { return (string)GetValue(CloseButtonTextProperty); }
set { SetValue(CloseButtonTextProperty, value); }
}
public UserDefinedDialogView()
{
this.InitializeComponent();
}
}
The UserControl's xaml:
<UserControl
x:Class="HHPM_NEXT.Views.Common.UserDefinedDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HHPM_NEXT.Views.Common"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<ContentDialog x:Name="UserDefinedDialog"
Title="{x:Bind DialogTitle, Mode=TwoWay}"
Content="{x:Bind DialogContent, Mode=TwoWay}"
PrimaryButtonText="{x:Bind DialogPrimaryButtonText, Mode=TwoWay}"
CloseButtonText="{x:Bind DialogCloseButtonText, Mode=TwoWay}"
x:FieldModifier="Public">
</ContentDialog>
</UserControl>
Example Implementation (References to Zeroize are in a ViewModel, but I didn't add it because I didn't want this to get too long):
<views1:UserDefinedDialogView x:Name="ConfimationDialog" DialogTitle="{Binding ZeroizeTitle, Mode=TwoWay}" DialogContent="{Binding ZeroizeContent, Mode=TwoWay}"
DialogPrimaryButtonText="{Binding ZeroizeConfirmButtonText, Mode=TwoWay}" DialogCloseButtonText="{Binding ZeroizeCloseButtonText, Mode=TwoWay}"/>
I want to be able to change a property in my main window from my user controls view model.
This is the connection
MainWindow
I bind my property from its view model to my usercontrol
MainWindowViewModel
My property lies here, it does get updated when user control property changes
UserControl1
its dependency property that's binded to Main Window View Model returns a value from UserControlViewModel
UserControl1ViewModel
The logic that changes the property (which is supposed to update MainWindowViewModel) lies here.
I can do the binding between all of them, but the problem is when I update my property from the bottom layer (UserControlViewModel), it does not update my property neither in UserControl or in my MainWindowViewModel.
Here is all my code (I have also uploaded the project on my google drive)
MainWindow.xaml
<Window x:Class="WpfApplicationViewToViewModel.MainWindow"
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplicationViewToViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="367" Width="624">
<StackPanel>
<local:UserControl1 TextInUserControl="{Binding DataContext.TextInMainWindowViewModel,
Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
</local:UserControl1>
<Button Content="Test MainWindow VM" Command="{Binding CommandTestMWVM}" ></Button>
<Separator></Separator>
</StackPanel>
</Window>
MainVindow.xaml.cs
using System.Windows;
namespace WpfApplicationViewToViewModel
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
}
MainWindowViewModel.cs
using System;
using System.Windows;
using System.Windows.Input;
namespace WpfApplicationViewToViewModel
{
class MainWindowViewModel : ViewModelBase
{
public string TextInMainWindowViewModel
{
get
{
return _textInMainWindowViewModel;
}
set
{
_textInMainWindowViewModel = value;
RaisePropertyChanged("TextInMainWindowViewModel");
}
}
private string _textInMainWindowViewModel { get; set; }
//test button
public MainWindowViewModel()
{
_commandTestMWVM = new RelayCommand(new Action<object>(TestMWVM));
}
#region [Command] CommandTestMWVM
public ICommand CommandTestMWVM
{
get { return _commandTestMWVM; }
}
private ICommand _commandTestMWVM;
private void TestMWVM(object obj)
{
TextInMainWindowViewModel = TextInMainWindowViewModel + "MWVM";
MessageBox.Show("TextInMainWindowModel " + TextInMainWindowViewModel);
}
#endregion
}
}
UserControl1.xaml (includes just two buttons for testing purposes)
<UserControl x:Class="WpfApplicationViewToViewModel.UserControl1"
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:WpfApplicationViewToViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<Button Content="Test UC" Click="Button_Click"></Button>
<Button Content="Test UCVM" Command="{Binding CommandTestUCVM}" ></Button>
</StackPanel>
</UserControl>
UserControl1.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfApplicationViewToViewModel
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
private UserControl1ViewModel VM = new UserControl1ViewModel();
public UserControl1()
{
InitializeComponent();
this.DataContext = VM;
//http://stackoverflow.com/questions/15132538/twoway-bind-views-dependencyproperty-to-viewmodels-property
//does not work because breaks binding somewhere
//string propertyInViewModel = "TextInUserControlViewModel";
//var bindingViewMode = new Binding(propertyInViewModel) { Mode = BindingMode.TwoWay };
//this.SetBinding(TextInUserControlProperty, bindingViewMode);
}
//dependency property declaration
public static DependencyProperty TextInUserControlProperty =
DependencyProperty.Register("TextInUserControl",
typeof(string),
typeof(UserControl1)
);
public string TextInUserControl
{
get {
return (DataContext as UserControl1ViewModel).TextInUserControlViewModel;
}
set
{
(DataContext as UserControl1ViewModel).TextInUserControlViewModel = value;
this.SetValue(TextInUserControlProperty, value);
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
TextInUserControl = TextInUserControl + "UC";
MessageBox.Show("TextInUserControl : " + TextInUserControl);
}
}
}
UserControl1ViewModel.cs
using System;
using System.Windows;
using System.Windows.Input;
namespace WpfApplicationViewToViewModel
{
class UserControl1ViewModel : ViewModelBase
{
private string _textInViewModel;
public string TextInUserControlViewModel
{
get { return _textInViewModel; }
set {
_textInViewModel = value;
RaisePropertyChanged("TextInUserControlViewModel");
} }
//test button
public UserControl1ViewModel()
{
_commandTestUCVM = new RelayCommand(new Action<object>(TestUCVM));
}
#region [Command] CommandTestUCVM
public ICommand CommandTestUCVM
{
get { return _commandTestUCVM; }
}
private ICommand _commandTestUCVM;
private void TestUCVM(object obj)
{
TextInUserControlViewModel = TextInUserControlViewModel + "UCVM";
MessageBox.Show("TextInUserControlViewModel : " + TextInUserControlViewModel);
}
#endregion
}
}
Any help is really really appreciated because I've been trying to figure out this system (reading usercontrols viewmodel from mainwindow) for almost a week.
To make my question more clear:
TextInUserControl <=> TextInMainWindowViewModel : works succesfuly
TextInUserControl => TextInUserControlViewModel : works but when I change TextInUserControlViewModel, TextInUserControl doesn't get updated automatically.
Is there anyway I can let TextInUserControl know that TextInUserControlViewModel is changed?
You are setting your UserControl's DataContext to a UserControl1ViewModel instance, then binding the TextInUserControl property to DataContext.TextInMainWindowViewModel, which is resulting in it looking for the property UserControl1ViewModel.DataContext.TextInMainWindowViewModel, which does not exist.
One of the first rules of working with WPF/MVVM : NEVER set this.DataContext = x; in the code behind a user-control unless you intend to never pass that control any outside value.
Instead what you probably want is to add an instance of UserControl1ViewModel onto MainWindowViewModel, and bind the UserControl.DataContext to that instance.
For example,
class MainWindowViewModel : ViewModelBase
{
// add this property
public UserControl1ViewModel UserControlData { ... }
public string TextInMainWindowViewModel { ... }
public ICommand CommandTestMWVM { ... }
}
<!-- change binding to this -->
<local:UserControl1 DataContext="{Binding UserControlData}" />
and get rid of the following in your UserControl constructor
this.DataContext = VM;
You should call RaisePropertyChanged("TextInMainWindowViewModel"); in your MainWindowViewModel
I've fixed the problem by using a "bridge property". I copy the solution that might help the others having the same problem:
UserControl1.xaml.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfApplicationViewToViewModel
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.DataContext = new UserControl1ViewModel();
/*
[Bridge Binding ©]
It's not possible to bind 3 properties.
So this bridge binding handles the communication
*/
string propertyInViewModel = "TextInUserControlViewModel";
var bindingViewMode = new Binding(propertyInViewModel);
bindingViewMode.Mode = BindingMode.TwoWay;
this.SetBinding(BridgeBetweenUCandVWProperty, bindingViewMode);
}
#region Bridge Property
public static DependencyProperty BridgeBetweenUCandVWProperty =
DependencyProperty.Register("BridgeBetweenUCandVW",
typeof(string),
typeof(UserControl1),
new PropertyMetadata(BridgeBetweenUCandVWPropertyChanged)
);
public string BridgeBetweenUCandVW
{
get
{
return (string)GetValue(BridgeBetweenUCandVWProperty);
}
set
{
this.SetValue(BridgeBetweenUCandVWProperty, value);
}
}
private static void BridgeBetweenUCandVWPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((UserControl1)d).TextInUserControl = (string)e.NewValue;
}
#endregion
#region TextInUserControl Property
public static DependencyProperty TextInUserControlProperty =
DependencyProperty.Register("TextInUserControl",
typeof(string),
typeof(UserControl1),
new PropertyMetadata(OnTextInUserControlPropertyChanged)
);
private static void OnTextInUserControlPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((UserControl1ViewModel)((UserControl)d).DataContext).TextInUserControlViewModel = (string)e.NewValue;
}
public string TextInUserControl
{
get {
return (string)GetValue(TextInUserControlProperty);
}
set
{
this.SetValue(TextInUserControlProperty, value);
}
}
#endregion
private void Button_Click(object sender, RoutedEventArgs e)
{
TextInUserControl += "[UC]";
MessageBox.Show("TextInUserControl : " + TextInUserControl);
}
}
}
I have tried all morning to get this to work with no luck. I am using DynamicDataDisplay (D3) to display a graph. Here is my simple view defined using xaml.
<Window x:Class="BMSVM_Simulator.View.GraphWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:BMSVM_Simulator.ViewModel"
xmlns:d3="http://research.microsoft.com/DynamicDataDisplay/1.0"
x:Name="ThisGraphWindowInstance"
Title="Plot" Height="500" Width="750"
WindowStartupLocation="CenterOwner"
Icon="../res/qualcomm_q_icon.ico.ico"
MinWidth="400" MinHeight="300">
<Window.DataContext>
<ViewModel:GraphWindowPresenter/>
</Window.DataContext>
<Grid>
<d3:ChartPlotter Name="plotter" Margin="10,10,20,10">
<d3:InjectedPlotter Name="innerPlotter" Background="Aqua" SetViewportBinding="False">
<d3:VerticalAxis Placement="Right"/>
<d3:VerticalAxisTitle Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis2}" Placement="Right"/>
</d3:InjectedPlotter>
<d3:Header FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=title}"/>
<d3:VerticalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis2}"/>
<d3:HorizontalAxisTitle FontFamily="Arial" Content="{Binding ElementName=ThisGraphWindowInstance, Path=title}"/>
</d3:ChartPlotter>
</Grid>
</Window>
The issue is that the:
<d3:VerticalAxisTitle Content="{Binding ElementName=ThisGraphWindowInstance, Path=yAxis2}" Placement="Right"/>
in the InjectedPlotter does not display at all when I use the current setup with the Content bound to Path=yAxis2. I set at breakpoint and I see that yAxis2 is actually a defined string and that it is not null.
When I actually hardcode a value such that Content="DEFAULT TITLE", so it then becomes :
<d3:VerticalAxisTitle Content="DEFAULT TITLE" Placement="Right"/>
the title displays fine.
Does anyone know why this is happening?
Here is the code behind for reference:
public static readonly DependencyProperty yAxis2Property =
DependencyProperty.Register("yAxis2", typeof(string), typeof(GraphWindowView));
public string yAxis2
{
get { return (string)GetValue(yAxis2Property); }
set { SetValue(yAxis2Property, value); }
}
public void ShowGraph()
{
// consume ChartData
this.yAxis1 = ChartData.yAxisTitle1;
this.yAxis2 = "AXIS 2 TITLE..SHOW UP!";
.....
}
EDIT >>>>>>>>>
using BMSVM_Simulator.ViewModel;
using Microsoft.Research.DynamicDataDisplay;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using Microsoft.Research.DynamicDataDisplay.Navigation;
using Microsoft.Research.DynamicDataDisplay.PointMarkers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BMSVM_Simulator.View
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class GraphWindowView : Window
{
#region Fields
private readonly int DEFAULT_AXIS_WIDTH = 20;
private readonly Pen[] colors = {
new Pen(Brushes.Blue, 2),
new Pen(Brushes.DarkGreen, 2),
new Pen(Brushes.DarkMagenta, 2),
new Pen(Brushes.DarkSalmon, 2),
new Pen(Brushes.Maroon, 2),
new Pen(Brushes.Orange, 2),
new Pen(Brushes.SkyBlue, 2)
};
#endregion
#region DependencyProperties
public static readonly DependencyProperty yAxis1Property =
DependencyProperty.Register("yAxis1", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty yAxis2Property =
DependencyProperty.Register("yAxis2", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty titleProperty =
DependencyProperty.Register("title", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty xAxisProperty =
DependencyProperty.Register("xAxis", typeof(string), typeof(GraphWindowView));
public static readonly DependencyProperty DatesProperty =
DependencyProperty.Register("Dates", typeof(EnumerableDataSource<int>), typeof(GraphWindowView));
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(EnumerableDataSource<int>), typeof(GraphWindowView));
public static readonly DependencyProperty ChartDataProperty =
DependencyProperty.Register("ChartData", typeof(ChartData), typeof(GraphWindowView));
public static readonly DependencyProperty rightAxisWidthProperty =
DependencyProperty.Register("rightAxisWidth", typeof(int), typeof(GraphWindowView));
public int rightAxisWidth
{
get { return (int)GetValue(rightAxisWidthProperty); }
set { SetValue(rightAxisWidthProperty, value); }
}
public ChartData ChartData
{
get { return (ChartData)GetValue(ChartDataProperty); }
set { SetValue(ChartDataProperty, value); }
}
public EnumerableDataSource<int> Dates
{
get { return (EnumerableDataSource<int>)GetValue(DatesProperty); }
set { SetValue(DatesProperty, value); }
}
public EnumerableDataSource<int> Data
{
get { return (EnumerableDataSource<int>)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public string xAxis
{
get { return (string)GetValue(xAxisProperty); }
set { SetValue(xAxisProperty, value); }
}
public string yAxis1
{
get { return (string)GetValue(yAxis1Property); }
set { SetValue(yAxis1Property, value); }
}
public string title
{
get { return (string)GetValue(titleProperty); }
set { SetValue(titleProperty, value); }
}
public string yAxis2
{
get { return (string)GetValue(yAxis2Property); }
set { SetValue(yAxis2Property, value); }
}
#endregion
public GraphWindowView()
{
InitializeComponent();
rightAxisWidth = DEFAULT_AXIS_WIDTH;
}
public void ShowGraph()
{
// consume ChartData
this.xAxis = ChartData.xAxisTitle;
this.yAxis1 = ChartData.yAxisTitle1;
this.yAxis2 = "AXIS 2 TITLE..SHOW UP!"; // ChartData.yAxisTitle2;
this.title = ChartData.title;
this.rightAxisWidth = DEFAULT_AXIS_WIDTH;
// list of data points
List<DataSet> dataSets = this.ChartData.dataPoints;
int colorCounter = 0;
int rightAxisCount = 0;
foreach (DataSet set in dataSets)
{
set.dates.SetXMapping(x => x);
set.data.SetYMapping(x => x);
CompositeDataSource compositeDataSource1 = new
CompositeDataSource(set.dates, set.data);
if (set.axis == AxisSide.LEFT)
{
plotter.AddLineGraph(compositeDataSource1, colors[colorCounter % colors.Length],
new CirclePointMarker { Size = 8.00, Fill = Brushes.Red },
new PenDescription(set.legendTitle));
}
else
{
innerPlotter.AddLineGraph(compositeDataSource1, colors[colorCounter % colors.Length],
new CirclePointMarker { Size = 8.00, Fill = Brushes.Red },
new PenDescription(set.legendTitle));
rightAxisCount++;
}
colorCounter++;
}
// if there is nothing plotted against the right axis, don't show it
if (rightAxisCount == 0)
{
rightAxisWidth = 0;
}
plotter.Viewport.FitToView();
// there are duplicate legends, so we hide one
plotter.LegendVisibility = Visibility.Hidden;
Show();
}
}
}
In the code you provided, you have set the datacontext as an object of GraphWindowPresenter, but while declaring the dependancy property you have set the GraphWindowView object. Please make sure that you set the appropriate object as datacontext
<Window.DataContext>
< ViewModel:GraphWindowPresenter/>
< /Window.DataContext>
DependencyProperty.Register("yAxis2", typeof(string), typeof(GraphWindowView))
Try :
<d3:VerticalAxisTitle Content="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=yAxis2,Mode=OneWay}" />
I ran a quick test binding a TextBox.Text, and the code you have posted works.
<Window x:Class="WpfApplication2.MainWindow"
x:Name="TestWindow" ...>
<StackPanel>
<!-- both bindings work -->
<TextBlock Text="{Binding ElementName=TestWindow, Path=yAxis2}" />
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}, Path=yAxis2}" />
</StackPanel>
</Window>
public partial class MainWindow : Window
{
public static readonly DependencyProperty yAxis2Property =
DependencyProperty.Register("yAxis2", typeof(string), typeof(MainWindow));
public string yAxis2
{
get { return (string)GetValue(yAxis2Property); }
set { SetValue(yAxis2Property, value); }
}
public MainWindow()
{
InitializeComponent();
this.yAxis2 = "TESTING";
}
}
So my best guess is that either
you are not calling ShowGraph() on the window
or the VerticalAxisTitle object is not one that exists in the Visual Tree, much like some other WPF objects like DataGridColumn
To determine if the first issue is your problem, simply ensure you are calling ShowGraph() in the constructor behind your window, or just set yAxis2 the way I have here for testing.
You could also use a tool like Snoop that is very useful for debugging runtime databindings.
If that is done and it's still not showing up correctly, then you may need to do some more research into the VerticalAxisTitle to find workarounds on how to bind it correctly. If you have trouble finding anything specific to VerticalAxisTitle, try looking up how it's done for DataGridColumn, such as this answer.
(As a side note, it's a standard convention to capitalize public properties, so your property should be YAxis2. Just my OCD kicking in.) :)
I have a problem with binding my usercontrol to object in mainwindow. I don't know what is wrong.
I created custom control MyUserControl which has editable textbox
MyUsrControl.xaml
<UserControl x:Class="UserControlToObject.MyUsrControl"
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="70" d:DesignWidth="200">
<StackPanel Orientation="Horizontal">
<Label Grid.Row="0" Grid.Column="0" Margin="5">Name</Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="5" Name="tbxName"></TextBox>
</StackPanel>
</UserControl>
Next I defined DP to allow modify this textbox outside countrol
MyUsrControl.xaml.cs
namespace UserControlToObject
{
public partial class MyUsrControl : UserControl
{
public static readonly DependencyProperty EmpNameProperty = DependencyProperty.Register("EmpNameProperty", typeof(string), typeof(MyUsrControl),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, EmpNamePropertyChanged));
public string EmpName
{
get
{
return (string)GetValue(EmpNameProperty);
}
set
{
SetValue(EmpNameProperty, value);
}
}
public MyUsrControl()
{
InitializeComponent();
}
static void EmpNamePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
MyUsrControl x = (MyUsrControl)sender;
x.tbxName.Text = (string)e.NewValue;
}
}
}
Next, I defined Employee class - properties of object of this class will be displayed on user control
namespace UserControlToObject
{
/// <summary>
/// Employee class
/// </summary>
class Employee : INotifyPropertyChanged
{
string m_name, m_surname;
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Employee name property
/// </summary>
public string Name
{
get { return m_name; }
set
{
m_name = value;
OnPropertyChanged("Name");
}
}
/// <summary>
/// Employee surname property
/// </summary>
public string Surname
{
get { return m_surname; }
set
{
m_surname = value;
OnPropertyChanged("Surname");
}
}
public Employee()
{
m_name = "unknown name";
m_surname = "unknown surname";
}
public Employee(string name, string surname)
{
m_name = name;
m_surname = surname;
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
And finally MainWindow.xaml
<Window x:Name="myApp" x:Class="UserControlToObject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UserControlToObject"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:MyUsrControl x:Name="ucEmp" EmpName="{Binding Name}"></local:MyUsrControl>
<Label Content="{Binding ElementName=ucEmp, Path=EmpName}"></Label>
</StackPanel>
</Window
>
MainWindow.xaml.cs
namespace UserControlToObject
{
public partial class MainWindow : Window
{
Employee anEmployee;
public MainWindow()
{
InitializeComponent();
anEmployee = new Employee("John", "Wayne");
this.DataContext = anEmployee;
}
}
}
This line don't work (error saying that I can set binding only on DependencyProperty... of DependencyObject):
<local:MyUsrControl x:Name="ucEmp" EmpName="{Binding Name}"></local:MyUsrControl>
Those settings below works, so I think that's problem with my Employee class (sth is missing ?)
<local:MyUsrControl x:Name="ucEmp" EmpName="John"></local:MyUsrControl> --> set EmpName ok
<Label Content="{Binding ElementName=ucEmp, Path=EmpName}"></Label> --> get EmpName ok
I've no idea whot is wrong, so will be very greatfull for help
I did the same but binding TextBox instead of user control and was no problem.
TextBox.Text is dependency property same as my Employee.EmpName
When registering the dependency property you need to use the name that you want to use in XAML. In your case, this is EmpName and not EmpNameProperty:
public static readonly DependencyProperty EmpNameProperty = DependencyProperty.
Register(nameof(EmpName), typeof(string), typeof(MyUsrControl), new
FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.
BindsTwoWayByDefault, EmpNamePropertyChanged));
"EmpNameProperty" in your property registeration should be ""EmpName"" w.
Thanks
I'm having difficulties with databinding on my custom user control (s). I created an example project to highlight my problem. I'm completely new to WPF and essentially MVVM as well, so bear with me...
I created a simple view that uses databinding two ways. The databinding on the built-in control works just fine. My custom control doesn't... I put a breakpoint in the PropertyChangedCallback of my control. It gets hit once on startup, but then never again. Meanwhile, the label I have bound to the same value is happily counting down.
What am I missing? My example project follows:
The main window:
<Window x:Class="WpfMVVMApp.MainWindow"
xmlns:local="clr-namespace:WpfMVVMApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.DataContext>
<local:CountdownViewModel />
</Grid.DataContext>
<Label Name="custName" Content="{Binding Path=Countdown.ChargeTimeRemaining_Mins}" Height="45" VerticalAlignment="Top"></Label>
<local:UserControl1 MinutesRemaining="{Binding Path=Countdown.ChargeTimeRemaining_Mins}" Height="45"></local:UserControl1>
</Grid>
</Window>
Here's my model:
namespace WpfMVVMApp
{
public class CountdownModel : INotifyPropertyChanged
{
private int chargeTimeRemaining_Mins;
public int ChargeTimeRemaining_Mins
{
get
{
return chargeTimeRemaining_Mins;
}
set
{
chargeTimeRemaining_Mins = value;
OnPropertyChanged("ChargeTimeRemaining_Mins");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
The ViewModel:
namespace WpfMVVMApp
{
public class CountdownViewModel
{
public CountdownModel Countdown { get; set; }
DispatcherTimer timer;
private const int maxMins = 360;
public CountdownViewModel()
{
Countdown = new CountdownModel { ChargeTimeRemaining_Mins = 60 };
// Setup timers
timer = new DispatcherTimer();
timer.Tick += new EventHandler(this.SystemChargeTimerService);
timer.Interval = new TimeSpan(0, 0, 1);
timer.Start();
}
private void SystemChargeTimerService(object sender, EventArgs e)
{
//convert to minutes remaining
// DEMO CODE - TODO: Remove
this.Countdown.ChargeTimeRemaining_Mins -= 1;
}
}
}
Here's the XAML for my user control:
<UserControl x:Class="WpfMVVMApp.UserControl1"
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">
<Grid>
<Label Name="Readout"></Label>
</Grid>
</UserControl>
And here's the code behind the user control:
namespace WpfMVVMApp
{
public partial class UserControl1 : UserControl
{
#region Dependency Properties
public static readonly DependencyProperty MinutesRemainingProperty =
DependencyProperty.Register
(
"MinutesRemaining", typeof(int), typeof(UserControl1),
new UIPropertyMetadata(10, new PropertyChangedCallback(minutesRemainChangedCallBack))
);
#endregion
public int MinutesRemaining
{
get
{
return (int)GetValue(MinutesRemainingProperty);
}
set
{
SetValue(MinutesRemainingProperty, value);
}
}
static void minutesRemainChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
UserControl1 _readout = (UserControl1)property;
_readout.MinutesRemaining = (int)args.NewValue;
_readout.Readout.Content = _readout.MinutesRemaining;
}
public UserControl1()
{
InitializeComponent();
}
}
}
Your change callback is breaking the binding.
As a skeleton: in your window you have UC.X="{Binding A}" and then in that property change (in UC) you have X=B;. This breaks the binding since in both cases you set X.
To rectify, remove change callback and add this to the label:
Content="{Binding MinutesRemaining, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
I tried your code works fine the only change i made was to remove the code behind propertychangedcallback you have and databind the Label (Readout) to the dependency property.
USERCONTROL(XAML)
<UserControl x:Class="WpfApplication1.UserControl"
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">
<Grid>
<Label Name="Readout" Content="{Binding RelativeSource={RelativeSource
AncestorType=UserControl}, Path=MinutesRemaining}"/>
</Grid>
</UserControl>
USERCONTROL (CODE BEHIND)
public partial class UserControl1 : UserControl
{
#region Dependency Properties
public static readonly DependencyProperty MinutesRemainingProperty =
DependencyProperty.Register
(
"MinutesRemaining", typeof(int), typeof(UserControl1),
new UIPropertyMetadata(10)
);
#endregion
public int MinutesRemaining
{
get
{
return (int)GetValue(MinutesRemainingProperty);
}
set
{
SetValue(MinutesRemainingProperty, value);
}
}
public UserControl1()
{
InitializeComponent();
}
}