Is there a way, to bind the series of a OxyPlot in XAML, if I don't know, how many series I will have?
I know, that I can create a PlotModel, maybe I can bind a Collection of Series. But what I am realy looking for is, if I can bind the series to List of doubles.
Possible ItemSources examples:
ObservableCollection<Tupel<double, List<double>>> ItemSource1 { get; set; }
ObservableCollection<Tupel<double, double>> ItemSource2 { get; set; }
Possible Xaml Code:
<oxy:Plot>
<oxy:LineSeries ItemSource="{Binding ItemSource}" />
</oxy:Plot>
I dind't find such a use case in the examples. Does someone have maybe a tipp for me?
Is there a way, to bind the series of a OxyPlot in XAML, if I don't know, how many series I will have?
No, there isn't.
Does someone have maybe a tipp for me?
As you have already discovered, you could create a PlotModel in your view model, and bind to and add series to this one.
There is a code sample available in the official docs:
public class MainViewModel
{
public MainViewModel()
{
this.MyModel = new PlotModel { Title = "Example 1" };
this.MyModel.Series.Add(new FunctionSeries(Math.Cos, 0, 10, 0.1, "cos(x)"));
}
public PlotModel MyModel { get; private set; }
}
XAML:
<oxy:PlotView Model="{Binding MyModel}"/>
If you don't want to use a PlotModel, you could create an attached behaviour that adds the series.
Related
In WPF using MVVM can we bind a textbox to a property of a specific instance , that is to one particular object ?
The short answer? Yes, what you are describing is kind of the purpose behind databinding.
Here is the extended answer in form of an example.
<Grid DataContext={Binding myViewModel}>
<TextBlock Text={Binding myTextProperty}/>
</Grid>
XAML.cs
public class Main{
public ViewModelObject myViewModel { get; set; }
public void Main(){
InitializeComponent();
myViewModel = new ViewModelObject();
this.DataContext = this;
}
}
ViewModelObject.cs
public class ViewModelObject{
public string myTextProperty{
get { return "It works!"; }
}
}
This will work. If you want a deeper example or understanding you need to research the following.
How WPF works
XAML
DataBinding
INotifiyPropertyChanged
MVVM
interface ICar
{
UserControl SmallView{ get; }
UserControl CompleteView{ get; }
}
class ViewModel
{
ObservableCollection<ICar> Cars{ get; set;}
ObservableCollection<UserControl> SmallViews{ get; }
ObservableCollection<UserControl> CompleteViews{ get; }
}
XAML
<ItemControl ItemsSource="{Binding SmallViews}"/>
<ItemControl ItemsSource="{Binding CompleteView}"/>
I am adding ICars instances to ViewModel.Cars collection. When that happens I want the two UserControls (small and Complete) to be added in the View (XAML).
-I can get it to work as I want, by setting the ItemsSources in CodeBehind when Cars.CollectionChanged is Raised. But I fear all the collection is redrawn for all items in ItemsSource.. I only want the changes to be added, and I would like en elegant solution without a lot of CodeBehind.
This Codebehind makes it work as intended - but I would like something cleaner somthing with real Binding.
CompleteControls and SmallControls are the names for the ItemControls above, which in this solution has no binding markup :-( .
public CarsView(ViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
UpdateViews(viewModel.Cars);
viewModel.Cars.CollectionChanged += (caller, args) =>
UpdateViews(args.NewItems.Cast<ICar>());
}
private void UpdateViews(IEnumerable<ICar> newCars)
{
foreach (var car in newCars)
{
CompleteControls.Items.Add(car.CompleteView);
SmallControls.Items.Add(car.SmallView);
}
}
I see a couple of flaws in your concepts.
class ViewModel
{
ObservableCollection<ICar> Cars{ get; set;}
// This is wrong for MVVM. You don't need this.
//
// ObservableCollection<UserControl> SmallViews{ get; }
// ObservableCollection<UserControl> CompleteViews{ get; }
}
It will also get rid of this.
UpdateViews(viewModel.Cars);
viewModel.Cars.CollectionChanged += (caller, args) =>
UpdateViews(args.NewItems.Cast<ICar>());
About the ObvservableCollection you need to know 2 things.
If you create it directly from another collection it will not trigger the event.
Your args.NewItems.Cast() will always produce one item only in a collection since the ObservableCollection does not have a AddRange() method.
I see that you came from Winforms. Start by inspecting more about MVVM. it will pay off very fast. You need to remember that if you are doing anything in the code behind with controls, stop doing it, cause your doing it wrong.
You will instantiate the UserControls in XAML.
<Listbox ItemsSource={Binding Cars}>
<Listbox.ItemsTemplate>
<DataTemplate>
<StackPanel>
<my:SmallViews />
<my:CompleteViews />
</StackPanel>
</DataTemplate>
</Listbox.ItemsTemplate>
</Listbox>
You should really avoid ever dealing with any UI elements in code behind
I have a window with 3 textboxes in a grid -this is my view- and I have Save button to add a new user to my user list with the datas from the textboxes.
I want to use a relay command to do this on my viewmodel class but I am quite confused with how to make the bindings. I hope it's clear enough. Any ideas, or examples will be helpful.
thanks in advance.
You should have a ViewModel something like the following :
class UserViewModel
{
public String Name { get; set; }
public String Password { get; set; }
public String Email { get; set; }
public RelayCommand AddUserCommand { get; set; }
public UserViewModel()
{
AddUserCommand = new RelayCommand(AddUser);
}
void AddUser(object parameter)
{
// Code to add user here.
}
}
And you can use it like following :
<StackPanel>
<TextBox Text="{Binding Name}"></TextBox>
<TextBox Text="{Binding Password}"></TextBox>
<TextBox Text="{Binding Email}"></TextBox>
<Button Command="{Binding AddUserCommand}">Add</Button>
</StackPanel>
To make this work, put following code in your UserControl/Control/Window's constructor :
DataContext = new UserViewModel();
I presume that you read Josh Smith article: WPF Apps With The Model-View-ViewModel Design Pattern. If you didn't, then read it first, and then download code, because example is very similar to your problem.
Did you created an instance of the ViewModel and putted this instance in the DataContext of your view or stackpanel?
example:
UserViewModel viewModel = new UserViewModel();
UserWindow view = new UserWindow();
view.DataContext = viewModel;
view.Show();
There are several options on coupling the View and the Viewmodel:
Create the View and ViewModel and set the ViewModel to the DataContext property (code above)
Create the ViewModel in the constructor of the View and fill the DataContext property with it
Create a Resource in your view of the type of your ViewModel and fill the DataContext property in XAML
I prefer the first option because you can combine the Views and Viewmodels as you like at runtime.
Hopefully this is a helpfull answer.
I have next enumeration
Enum rcCategory
{
Incoming,
Internal,
Outgoing
}
and I have property "categories" in my class which has rcCategory[] type.
I would like to bind this property to the listBox. I use next code for this
MyListBox.SetBinding (ListBox.ItemsSource, new Binding {Source= myClass.categories});
But this code doesnt work as expected.
How Can I do this. My listBox always is empty but source property has value
See Bea Stollnitz top ranked article on it.
In short you need to bind to an ObjectProvider which calls the static method Enum.GetValues( typeof(YourEnum) ) to return the list.
http://bea.stollnitz.com/blog/?p=28
Update: Sorry got a slight speedreading issue. This one is easier.. Verified that it works. Recommended: Find up a copy of ProgrammingWPF and go thru the DataBinding chapter...
XAML:
<ListBox DockPanel.Dock="Left" ItemsSource="{Binding EnumArrayProp}"/>
Codebehind:
public partial class Window1 : Window
{
public rcCategory[] EnumArrayProp
{
get; set;
}
public Window1()
{
InitializeComponent();
this.EnumArrayProp = new rcCategory[] { rcCategory.Incoming, rcCategory.Incoming, rcCategory.Outgoing };
this.DataContext = this;
}
Pretty simple task, but the source code doesn't do required job... Please advise.
There is Products collection in the class (approach is based on the MVVm pattern, but that is not influe on the current issue):
public class ProductWindowViewModel : WorkspaceViewModel // implements INotifyPropertyChanged
{
public ProductWindowViewModel()
{
Products = new List<Product>(ProductService.Instance.Repository.GetAll());
}
List<Product> Products { get; set; }
}
Here is class declaration:
public class Product : IEntity
{
#region Public Properties
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Cost { get; set; }
#endregion
}
The class instance is binded to the window's Grid data context:
ProductWindow wnd = new ProductWindow();
wnd.MainGrid.DataContext = new ProductWindowViewModel();
wnd.ShowDialog();
And here is xaml code of the window:
<Window x:Class="WpfTest1.ProductWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ProductWindow" Height="300" Width="300" xmlns:igDP="http://infragistics.com/DataPresenter"
xmlns:ViewModel="clr-namespace:ViewModel;assembly=ViewModel">
<Grid x:Name="MainGrid">
<Grid.Resources>
<ObjectDataProvider x:Key="odpObjectDataProvider" ObjectType="{x:Type ViewModel:ProductWindowViewModel}" />
</Grid.Resources>
<Grid DataContext="{StaticResource odpObjectDataProvider}">
<igDP:XamDataGrid DataSource="{Binding Path=Products}"/>
</Grid>
</Grid>
The xamDataGrid sampe is the same. overall code is pretty simple, but doesn't work.
Does anybody know why? Any thoughts are welcome.
How could I debug binding to resolve the problem himselft?
Thanks.
Ok, maybe this won't exactly answer your question, but it looks like you are instantiating your viewmodel class twice. Once in your code, immediately after creating your window, and once in the ObjectDataProvider thing. It will probably be easier to debug if you settle on one of them. Suggestion:
1. Comment out this line: wnd.MainGrid.DataContext = new ProductWindowViewModel();
2. Set a breakpoint inside the constructor of your viewmodel
3. Start it up and see if the breakpoint gets hit. If it does get hit, you know you are doing something right.
Also, check the output window in visual studio and see if any binding exceptions are reported there.
In my case the code in the windows constructor is redundant. Now all is working, seems like removing unnecessary assignement resolved an issue.