I am creating a Charting application using SciChart.
I have added a chart modifier class which allows editing of the chart data but only the data currently displayed. I need to extend this class so that the full ObservableCollection of each XyDataSeries can be accessed.
I have implemented an attached property which I can bind to in the MainWindow DataContext however whenever I run the application the collection is showing as null in the modifier class. Please can you advise. Thanks
public class MoveBlockModifier : ChartModifierBase
{
public static readonly DependencyProperty XyFGDataProperty = DependencyProperty.RegisterAttached("XyFGData", typeof(ObservableCollection<XyDataSeries<double,double>>), typeof(MoveBlockModifier), new FrameworkPropertyMetadata(new ObservableCollection<XyDataSeries<double,double>>()));
public ObservableCollection<XyDataSeries<double, double>> XyFGData
{
get { return (ObservableCollection < XyDataSeries<double, double>>)GetValue(XyFGDataProperty); }
set { SetValue(XyFGDataProperty, value); }
}
public MoveBlockModifier()
{
_ghostSeries = new FastLineRenderableSeries()
{
Stroke = Colors.Black,
DataSeries = editingSeries,
Name = "GhostSeries",
StrokeThickness = 1,
Opacity = 0.75,
};
}
}
Public Class MainWindow: Window, INotifyPropertyChanged
{
private ObservableCollection<XyDataSeries<double, double>> _xyFGData;
public ObservableCollection<XyDataSeries<double, double>> XYFGData
{
get { return _xyFGData; }
set { _xyFGData = value; OnPropertyChanged("XYFGData"); }
}
}
XAML of MainWindow
<s:SciChartSurface x:Name="Chart2">
<s:SciChartSurface.ChartModifier>
<local:MoveBlockModifier FixStart="{Binding FixStart}" FixEnd="{Binding FixEnd}"
IsEnabled="{Binding ChartTwoMoveBlockEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
XyFGData="{Binding XYFGData, Mode=TwoWay}" />
</s:ModifierGroup>
</s:SciChartSurface.ChartModifier>
</s:SciChartSurface>
The question above seems incomplete / has some errors. You mention an attached property, which you define as this
public static readonly DependencyProperty XyFGDataProperty = DependencyProperty.RegisterAttached("XyFGData", typeof(ObservableCollection<XyDataSeries<double,double>>), typeof(MoveBlockModifier), new FrameworkPropertyMetadata(new ObservableCollection<XyDataSeries<double,double>>()));
public ObservableCollection<XyDataSeries<double, double>> XyFGData
{
get { return (ObservableCollection < XyDataSeries<double, double>>)GetValue(XyFGDataProperty); }
set { SetValue(XyFGDataProperty, value); }
}
...
but this isn't the way to define attached properties in WPF. Follow the MSDN documentation for how to register an attached property.
Secondly, you define a default value of new ObservableCollectionXyDataSeries<double, double> in your FrameworkPropertyMetadata, but this is a bad idea, because you will share one instance of ObservableCollectionXyDataSeries<double, double> statically across all instances of MoveBlockModifier. Have a look at Where to initialize reference type dependency properties for a custom control?
Lastly its an attached property that you want to define but in XAML you are not using it like an attached property.
This part:
is incorrect. See how an attached property is attached in XAML here.
Finally you bind MoveBlockModifier.XyFGData to a property XYFGData in your main window but the DataContext of the MoveBlockModifier might not be MainWindow.
I suggest starting again and fixing these errors!
Related
I have a list of objects (ObservableCollection subjectlist) and want to display them in a Combobox via data-binding and dependency property.
WPF Data Binding to a Combo Box
I searched on stackoverflow and tried to implement the solution of Craig Suchanec in the link above. (tried the whole day now and I just don't get what's wrong with my code)
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public static readonly DependencyProperty SubjectListProperty =
DependencyProperty.Register("SubjectList",
typeof(ObservableCollection<Subject>),
typeof(MainWindow));
private ObservableCollection<Subject> subjectList = new ObservableCollection<Subject>();
Initialization init1;
public ObservableCollection<Subject> SubjectList
{
get { return (ObservableCollection<Subject>)GetValue(SubjectListProperty); }
// get { return subjectList; }
}
public MainWindow()
{
init1 = new Initialization();
subjectList = init1.createMenuSubject();
InitializeComponent();
//this.comboBox.DataContext = SubjectList;
}
}
MainWindow.xaml
<Grid>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left"VerticalAlignment="Top" Width="120" Margin="321,10,0,0"
ItemsSource="{Binding ElementName=mainWindow, Path=SubjectList}" DisplayMemberPath="Name"/>
</Grid>
It DOES work if I just set the DataContext and work without dependency property, but as soon as I try to use the dependency property for data-binding it does NOT and I don't see the significant difference between my implementation and the solution given in the link.
It would be much appreciated, if somebody could help me with this problem.
I can't see anywhere in your code where you are actually setting the value of the SubjectList property.
You are however setting the value of subjectList, but you're binding to SubjectList. Note the casing difference.
You should write:
public ObservableCollection<Subject> SubjectList
{
set { base.SetValue(SubjectListProperty, value); }
get { return (ObservableCollection<Subject>)base.GetValue(SubjectListProperty); }
}
instead of
public ObservableCollection<Subject> SubjectList
{
set { base.SetValue(SubjectListProperty, value); }
get { return subjectList; }
}
or any other ad hoc format. You are setting subjectList in your constructor MainWindow(), however, it will not set the value of SubjectList (with Capital S) and a property change event is never raised. Remove subjectList.
If you are wondering why the DataContext approach works, you should note it will work even if you do not use a DepenedencyProperty. However, if you implement INotifyPropertyChange, it will work with setting ElementName too.
I have a control (a) that needs to show / hide another control (b) in it
(a) Has:
1- A reference to (b)
2- A dependency property for (b) viewmodel
(b) Has a Dependency property for its viewmodel Named ViewModel.
Here is the code:
For (a)
If I create that way All mi binds works pretty well, my problem is if I have many instances of (a), each one works as b is the same instance for all of them because it is a static property.
public partial class a : UserControl
{
public a()
{
}
public bVM b
{
get { return (bVM)GetValue(bProperty); }
set { SetValue(bProperty, value); }
}
public static readonly DependencyProperty bProperty =
DependencyProperty.Register("b", typeof(bVM), typeof(a)), new PropertyMetadata(new bVM()));
}
now if I create a bVM instance into (a) constructor, all my binds work fine except for command bindings.
public partial class a : UserControl
{
public a()
{
b = new bVM();
}
public bVM b
{
get { return (bVM)GetValue(bProperty); }
set { SetValue(bProperty, value); }
}
public static readonly DependencyProperty bProperty =
DependencyProperty.Register("b", typeof(bVM), typeof(a));
}
And here is my bind for (b) at (a) xaml:
<local:b
x:Name="bName"
ViewModel="{Binding ElementName=ThisAControl,Path=b}"/>
And this is my bind for command lost
<Button Content="Test"
Command="{BindingElementName=ThisBControl,Path=ViewModel.ExitCommand }" />
Why my command binds are lost second way?
What I'm doing wrong?
I agree with #dymanoid comment, that normally you should not have a dependency property for VM. Aside of this strange implementation, technically the reason why binding is lost is because in constructor you are breaking it with:
b = new bVM();
To set value for a dependency property inside dependency object you should you SetCurrentValue method, that will not break any binding.
SetCurrentValue(a.bProperty, new bVM());
MSDN:
The SetCurrentValue method changes the effective value of the property, but existing triggers, data bindings, and styles will continue to work.
I have a view which binds to a ViewModel
DataContext="{Binding MyViewModel, Source={StaticResource Locator}}">
the textboxes, ... are binding to the propertys IN the ViewModel:
<TextBox Text="{Binding MyValue, Mode=TwoWay}"/>
<TextBlock Text="{Binding DisplayValue}"/>
Property in ViewModel:
public MyViewModel()
{
DisplayValue = "0€";
MyValue = "0";
}
private string _myvalue;
public string MyValue
{
get
{
return _myvalue;
}
set
{
_myvalue = value;
ChangeValue();
RaisePropertyChanged(() => MyValue);
}
}
private string _displayvalue;
public string DisplayValue
{
get
{
return _displayvalue;
}
set
{
_displayvalue = value;
RaisePropertyChanged(() => DisplayValue);
}
}
private void ChangeValue()
{
//do something here and change the values of the property, e.g.:
DisplayValue = MyValue + "€";
}
This is just a snipped. I normally have ~50 properties IN THE VIEWMODEL and all the methods are also in the ViewModel (means RelayCommands AND methods, which will be called in the setter of ~50% of the properties).
As you can see, I'm not using any Model(s). Is this a normal way of using MVVM or should I create a new class and put all the properties/methods in the new class (Model)?... But how am I supposed to bind the elements in the view with the Properties in the Model, when the views DataContext is binded to the ViewModel?
Edit: To make it clear.
I have a TextBox and the TextBox is binded to a property in the ViewModel. Is this the correct way of using MVVM? Should I use a Model-Class only when I have a List (e.g. ComboBox) or also when I have several TextBox (which would be in my eyes kinda stupid and unnecessary)?
I hope I understand what you are trying to do. My solution comprises of the DependencyProperty that I use in the MVVM pattern, not the INotifyPropertyChanged.
Lets say, you have a model, that contains a property:
public class SymbolStatsModel
{
public string Symbol
{
get
{
return this._symbol;
}
set
{
this._symbol = value;
}
}
}
Then the corresponding ViewModel is going to be like this. Declare a property and a dependency property:
public string Symbol
{
get
{
return (string)GetValue(SymbolProperty);
}
set
{
SetValue(SymbolProperty, value);
}
}
public static readonly DependencyProperty SymbolProperty =
DependencyProperty.Register
(
"Symbol",
typeof(string),
typeof(SymbolStatsViewModel),
new FrameworkPropertyMetadata
(
string.Empty
)
);
And also create a property of the Model class(SymStatsModel) in the ViewModel:
public SymbolStatsModel SymbolStatsModel
{
get
{
return new SymbolStatsModel(Symbol);
}
set
{
this.Symbol = value.Symbol;
}
}
In that way, the values that you assign to the ViewModel Symbol Property are going to be assigned to the Model Property. Also, you can directly access the Model's properties from the View by accessing the property of the Model present in the ViewModel.
This may be a little hard to grasp, but this sure is the way to make the view communicate with the Model. On another thought, you can specify the property of the Model just like I have mentioned in my solution, while using the INotifyPropertyChanged. A little immature I guess, but you gave give it a thought.
"Divide and Conquer" should help you in maintainability
Try and find boundaries in your UI, logical groups, repeating parts.
Factor them out
You can always reset the DataContext, or use a more complex Binding Path like MyVm.SubVm.Property
Can someone help me with this question?) In My XAML I have Listbox element. I want to add my user property into it(in my case - ConnectorStyle)
My XAML code:
<ListBox ItemsSource="{Binding Nodes}" ItemsPanel="{StaticResource CanvasItemsPanelTemplate}"
ItemTemplate="{StaticResource NodePictureTemplate}"
ItemContainerStyle="{StaticResource CanvasItemStyle}"
ConnectorStyle="{StaticResource ConnectorLineStyle}"/>
In my Model I have prepared this property:
public partial class MainPage : UserControl
{
public static readonly DependencyProperty ConnectorStyleProperty = DependencyProperty.Register(
"ConnectorStyle", typeof(Style), typeof(NodePicture), null);
public MainPage()
{
InitializeComponent();
}
public Style ConnectorStyle
{
get { return (Style)GetValue(ConnectorStyleProperty); }
set { SetValue(ConnectorStyleProperty, value); }
}
}
But I is a mistake - Cannot resolve ConnectorStyle.
Is there a simple (or a right way ) way of doing this?
There are two ways to do this: Either you can write a subclass for the ListBox that adds the DependencyProperty or you can write an attached property.
In your case you probably want to write a subclass that adds the property. Try something like this:
public class MyListBox : ListBox
{
public static readonly DependencyProperty ConnectorStyleProperty = DependencyProperty.Register(
"ConnectorStyle", typeof(Style), typeof(MyListBox), null);
public Style ConnectorStyle
{
get { return (Style)GetValue(ConnectorStyleProperty); }
set { SetValue(ConnectorStyleProperty, value); }
}
}
This will add a new type of ListBox that you can add in your xaml code. It will have all the same properties as a regular ListBox, but it will also have the ConnectorStyle property.
If you need to respond to changes to the ConnectorStyle property in your listbox then you should change the code for the Dependency Property, but that is outside the scope of this question.
And in XAML it shoul be :
<local:ListBoxEx
ConnectorStyle="{StaticResource ConnectorLineStyle}"/>
Consider the following Xaml
<Grid>
<TextBox>Text</TextBox>
<Button>Content</Button>
</Grid>
It will set the
Text Property of a TextBox (only WPF)
Content Property of a Button
Children Property of a Grid
But how is this specified? How do you specify which Property that goes between the opening and closing tag in Xaml?
Is this set by some metadata in the Dependency Property or what?
Thanks
There is a ContentPropertyAttribute that is applied to a class. WPF/Silverlight will use reflection to determine which property to use.
If you want to do this with a custom class, you can do it like so:
[ContentProperty("Bar")]
public class Foo : Control
{
public static DependencyProperty BarProperty = DependencyProperty.Register(
"Bar",
typeof(int),
typeof(Foo),
new FrameworkPropertyMetaData(0));
public int Bar
{
get { return (int)GetValue(BarProperty); }
set { SetValue(BarProperty, value); }
}
}
Then you could specify it in XAML like so:
<lcl:Foo>12</lcl:Foo>
Update
Since it is using reflection, you don't really need to do a DependencyProperty. For instance, this will also work:
[ContentProperty("Bar")]
public class Foo : Control
{
public int Bar { get; set; }
}