In part of a XAML code maximum and minimum values are set as follows:
<WindowsFormsHost>
<wf:NumericUpDown Maximum="12000" Minimum="120" x:Name="MyNumericUpDown" TextAlign="Right"/>
</WindowsFormsHost>
And in the same C# program inside a class I have the following:
namespace MyNameSpace
{
public class MyClass
{
public int max { get; } = 12000;
public int min { get; } = 120;
...
}
}
Is it possible to set the Maximum and Minimum values of NumericUpDown by using the class properties instead of hardcoding them? So that of I change class property values the XAML values autonomically updated.
Yup, it's possible, and even what I'd consider to be fundamental in WPF development. It's called data binding. I'll be implementing a simple one way data binding here which binds to a property on the code behind file (i.e MainView.xaml binds to MainView.xaml.cs), but you can bind to any class you'd like, this then forms the fundamentals of MVVM.
Xaml:
<Window x:Class="MainView"
Title="MainView"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<!-- Snip -->
</Window>
The important part is DataContext="{Binding RelativeSource={RelativeSource Self}}. Right here we're telling the window to bind to itself (i.e the code behind file). This is required for anything to work. You can use this Q&A to see how to bind to something else.
Continuing on:
<wf:NumericUpDown Maximum="{Binding Max}" Minimum="{Binding Min}" />
Here we're telling the NumericUpDown component to get its Maximum and Minimum values from the data context. Now, one caveat, you can only bind to properties and nothing else. So, if you ever run into a problem where your bindings aren't working, check to see if you're binding to a property or not. Here's the code of code behind file
public class MainWindow
{
public int Max { get; } = 12000;
public int Min { get; } = 120;
public MainWindow()
{
InitializeComponent();
}
}
This is a one way data binding, i.e the XAML can only read these values. For things like an input box, you'll need to use TwoWay bindings. And if anything changes those values (currently impossible, but may be in the future), then you need to implement INotifyPropertyChanged
P.S: No guarantees for the code. I just typed it out in this text box without any auto complete and the last time I did WPF is quite a while ago. Just leave a comment if something doesn't work and I'll try my best to fix it
I have 2 combobox, which include menus name and its details like (Pie-apple, chocolate), (Juice- apple, orange).
So if I select 'Pie' in the first combobox, second one should have its details-apple and chocolate.
Xaml code is below:
<DockPanel Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2" VerticalAlignment="Center" Margin="5">
<TextBlock Text="Menu : " HorizontalAlignment="Left" Height="32"/>
<ComboBox ItemsSource="{Binding LstMenu}" SelectedValue="{Binding SelectedMenu}" HorizontalAlignment="Left" Height="32"/>
<TextBlock Text="Detail: " HorizontalAlignment="Left" Height="32"/>
<ComboBox ItemsSource="{Binding LstDetail}" SelectedValue="{Binding SelectedDetail}" HorizontalAlignment="Left" Height="32" SelectionChanged="combobox_changed"/>
</DockPanel>
and viewmodel is below:
private List<string> lstMenu;
public List<string> LstMenu {
get { return lstMenu; }
set {
lstMenu = value;
RaisePropertyChanged("LstMenu");
}
private string selectedMenu;
public string SelectedMenu {
get { return selectedMenu; }
set {
selectedMenu= value;
RaisePropertyChanged("SelectedMenu");
LstDetails = new ObservableCollection<string>( //get its details list from DB );
}
private ObservableCollection<string> lstDetail;
public ObservableCollection<string> LstDetail {
get { return lstDetail; }
set {
lstDetail= value;
RaisePropertyChanged("LstDetail");
if (lstDetail != null && lstDetail.Count > 0) {
SelectedDetail = lstDetail.FirstOrDefault();
}
}
private string selectedDetail;
public string SelectedDetail {
get { return selectedDetail; }
set {
selectedDetail= value;
RaisePropertyChanged("SelectedDetail");
}
The problem is, if I selected Pie-apple and then changed the first combobox to Juice menu, Juice-apple is automatically selected but RaisePropertyChanged("SelectedDetail"); does not work.
I know that's because the selected two strings apple are same...
However I need to call the event SelectionChanged="combobox_changed" since it contains UI reload event.
Moreover, if I added IsAsync=True into the LstDetail combobox, it definitely works but SelectionChanged="combobox_changed" is called twice. I don't know why.
what should I do?
It's hard to know exactly what the architecture of your program is, without a good Minimal, Reproducible Example. But, from what you've posted, it seems you are trying to do too much of the work, and not letting WPF do its fair share. In particular, you seem to have just one "view model" type, and in that object you attempt to completely define everything that the user sees, and the state of that.
As has been noted in the comments, this has led to a situation where there are pieces of state that relate to each other, but where you haven't done enough work to make sure they stay synchronized with each other. You've delegated some of the work to WPF, but it doesn't have enough context to let you know when something important has changed, and so your UI winds up in a inconsistent state.
It would in fact be possible to fix your program as it stands now, by forcing the SelectedDetail property to refresh itself if the menu combo box changes. But a) I can't tell you exactly how to do that, because you've omitted all of the details that relate to the actual management of that property (such as the combobox_changed() event handler), and b) that's really just too much work anyway.
The first thing you need to get on board with, when writing WPF code, is to put as much of your program specification into declarations, and not procedures. WPF's binding mechanisms do a great job of automatically keeping view model data structures in sync with the actual UI. This means that you can view your program as two completely independent entities: the user interface itself, and the "business logic", i.e. the things your program actually has to do. The "view model" part mediates between these two elements. In the simplest WPF programs, the business logic itself can be entirely encapsulated in the view model data structures; in more complex applications, the view models focus on mapping between the UI ("view") and the business logic ("model").
This has an important implication: if you find yourself writing code that is directly interacting with the view element of your program — i.e. either responding to the UI or modifying it — that code had better be strictly specific to the view. Another way to look at that is, such code should be reusable with any other type of business logic, just as all of the built-in XAML stuff is completely reusable and not at all specific to your business logic.
Conversely, if that code you're writing is fiddling with the view model data structures directly or, even worse, is actually part of the view model data structures, you've gone off into the weeds. This should never happen.
You can use these two metrics to constantly evaluate as you go along whether you're designing the code correctly, and to help guide that design before you actually write the code.
Okay, with that little bit of indoctrination out of the way, here's how I would implement your stated goal:
You need some view models. Not just one, because you have a hierarchy of business logic objects, and the view models need to reflect that. Working from the bottom up:
You need a view model that can represent the detail to be displayed. For example:
class DetailViewModel : NotifyPropertyChangedBase
{
private string _name;
public string Name
{
get => _name;
set => _UpdateField(ref _name, value);
}
private string _description;
public string Description
{
get => _description;
set => _UpdateField(ref _description, value);
}
private decimal _price;
public decimal Price
{
get => _price;
set => _UpdateField(ref _price, value);
}
}
Notes:
The above relies on a base class NotifyPropertyChangedBase I use for all view models, which provides a convenient mechanism to implement observable properties. Code for that is provided below.
The above is strictly a simple data container. For this example, that's all that's needed, because all the example is concerning itself with is how to react to UI input, and WPF is great at managing that already, as long as it has a place to keep everything. A more interesting WPF program would have procedural aspects in the view model for providing commands that operate on the data beyond what the XAML is capable of defining.
Okay, so with a details data structure, we also need a place to keep a list of these objects for each type of menu in your program:
class MenuViewModel : NotifyPropertyChangedBase
{
private string _name;
public string Name
{
get => _name;
set => _UpdateField(ref _name, value);
}
private List<DetailViewModel> _menuItems = new List<DetailViewModel>();
public List<DetailViewModel> MenuItems
{
get => _menuItems;
set => _UpdateField(ref _menuItems, value);
}
private DetailViewModel _selectedItem;
public DetailViewModel SelectedItem
{
get => _selectedItem;
set => _UpdateField(ref _selectedItem, value);
}
}
You'll note that the above two view model data structures have a Name property. This is used to display to the user the name of the item they will be selecting.
The new aspect in this view model is the list of menu item objects, and then a property that keeps track of the currently selected menu item object. This is critical with respect to your question: in your implementation, the only thing you know about the currently selected item is its name. But when the same name appears on two different menus, you've got no way to distinguish the two. The only way out of that dilemma, given the design you chose, is to always refresh the details explicitly when the selected menu changes.
But here, we tie the selected item to the menu itself. This gives us two nice results:
When the menu changes, then whatever's bound to the selected item property will change as well, implicitly updating the displayed details, because WPF's binding engine understands the relationships of the properties involved. In particular, the details aren't just some random string, but rather a specific object that was retrieved from a different specific object. If that latter specific object is no longer the context for the binding (i.e. the user picks a new menu), then WPF knows that the former specific object needs to be re-evaluated.
By default, the user's selection for a given menu is remembered, because each menu has its own SelectedItem property! When the user selects an item from a menu, then selects a different menu, then after they are done with that second menu and go back to the first, the first will still have their previous selection from that menu. Now, this may or may not be the desired behavior. If not, it's reasonably easy in the view model to reset the selected item when the menu changes. But it's usually easier to suppress functionality than to create it, so having the default behavior provide that added functionality is nice.
Finally, of course, we need a place to keep track of the currently selected menu:
class MainViewModel : NotifyPropertyChangedBase
{
private List<MenuViewModel> _menus = new List<MenuViewModel>();
public List<MenuViewModel> Menus
{
get => _menus;
set => _UpdateField(ref _menus, value);
}
private MenuViewModel _selectedMenu;
public MenuViewModel SelectedMenu
{
get => _selectedMenu;
set => _UpdateField(ref _selectedMenu, value);
}
}
Just like with the menu object, this one has both a list of items (menus, in this case) and a property that keeps track of which specific item is selected.
Now that the view model data structures have been correctly designed to reflect the hierarchy of user selection in our user interface, it's a very simple matter to declare the user interface to work with those data structures:
<Window x:Class="TestSO58167153WpfTwoLevelDetail.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:l="clr-namespace:TestSO58167153WpfTwoLevelDetail"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate x:Key="comboBoxNameTemplate">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type l:DetailViewModel}">
<StackPanel>
<TextBlock Text="{Binding Description}"/>
<TextBlock Text="{Binding Price, StringFormat={}Price: ${0:0.00}}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ComboBox Grid.Row="0" Grid.Column="0" Width="100"
ItemsSource="{Binding Menus}" SelectedItem="{Binding SelectedMenu}"
ItemTemplate="{StaticResource comboBoxNameTemplate}"/>
<ComboBox Grid.Row="0" Grid.Column="1" Width="100" HorizontalAlignment="Left" Margin="10,0"
ItemsSource="{Binding SelectedMenu.MenuItems}" SelectedItem="{Binding SelectedMenu.SelectedItem}"
ItemTemplate="{StaticResource comboBoxNameTemplate}"/>
<ContentPresenter Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
Content="{Binding SelectedMenu.SelectedItem}"/>
</Grid>
<Window.DataContext>
<l:MainViewModel>
<l:MainViewModel.Menus>
<l:MenuViewModel Name="Pies">
<l:MenuViewModel.MenuItems>
<l:DetailViewModel Name="Apple" Description="Apple Pie with Pastry Crust" Price="10.50"/>
<l:DetailViewModel Name="Grasshopper" Description="Mint Pie with Oreo Crust" Price="17.95"/>
</l:MenuViewModel.MenuItems>
</l:MenuViewModel>
<l:MenuViewModel Name="Juice">
<l:MenuViewModel.MenuItems>
<l:DetailViewModel Name="Apple" Description="Refreshing Apple Juice" Price="3.70"/>
<l:DetailViewModel Name="Mango" Description="Sweet Mango Juice" Price="4.75"/>
</l:MenuViewModel.MenuItems>
</l:MenuViewModel>
</l:MainViewModel.Menus>
</l:MainViewModel>
</Window.DataContext>
</Window>
There are two main components to the above:
Data templates. These tell WPF how to map the view model data structure to elements in the UI. There are two here: a general-purpose one that just always shows, in a TextBlock element, the Name property of any view model data type; and a template that is specific to the DetailsViewModel object, and which displays just the values we're interested in as details.
The UI itself. This is super-simple: two ComboBox elements, providing the drop-down interface to select both a menu and an item on that menu; one ContentPresenter, a control whose main job is just to provide a place to render a data template for a given object; and a Grid object to organize it all. The ComboBox controls explicitly opt in to the data template that displays the item's Name property value, while the ContentPresenter infers the correct data template from the type of view model being used (but it also allows the template to be set explicitly, if you so desire).
The only other thing up there is the DataContext for the window itself, the content of which I've declared in the XAML here just because it's convenient for the purpose of the sample. In your real-world program, which appears to retrieve data from a database, you'd probably have the top-level view model handle populating itself based on that.
(Speaking of the data context: in the above, all binding paths are relative to the top-level view model. For the purpose of the sample, this is more convenient, but you of course have complete control over the data context for any element in the UI. An alternative way to implement this would be to set the DataContext properties for the controls lower in the dependency hierarchy, so that you don't have to repeat the top-level view model's property names in the binding paths.)
And that's all there is to it. You can compile and run the above code, and it will do just what you're asking for your code to do.
Minor notes:
All of the selections start out blank; you can of course initialize them to non-null values if you want, but doing so in the sample above would just add more code for no useful benefit, at least for the purpose of the sample.)
The view models here all use List<T> for their collections. This is fine for the example, because these collections never change. But as you likely already know, real-world WPF programs usually use ObservableCollection<T>, because generally they are including features that allow for those collections to be modified while the program runs. ObservableCollection<T> implements INotifyCollectionChanged, which in turn allows WPF to keep the UI in sync with the bound data. Feel free to replace List<T> with ObservableCollection<T> here or in any other similar scenario.
As promised, here's the code for the NotifyPropertyChangedBase class. There are lots of different ways to implement a base class like this, and in fact I have a different version with a couple more features that I typically use. But this one works well for a basic WPF example (indeed, for many even this one is too "feature-rich" :) ):
class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I'm using the Bing map SDK in my WPF application and the XAML looks like:
<m:Map
x:Name="MyMap"
Grid.Row="1"
CredentialsProvider="KEY"
ZoomLevel="{BINDING MapZoomLevel}"
Mode="Road">
The code behind:
private int mapZoomLevel;
public int MapZoomLevel { get { return mapZoomLevel; } set { mapZoomLevel = value; NotifyOfPropertyChange(() => MapZoomLevel); } }
But this aint working. I guessing it is because I've already bound the Map by setting x:Name. The problem is that I can't remove the x:Name since I'm doing some setup in the view but is there a workaround? I would like to be able to bind the ZoomLevel of the map somehow
In order to data bind, you need to do a few things:
1) You must set the DataContext of the UserControl or Window to the object that contains the property that you want to bind to. That could be like this (in the UserControl or Window code behind) if that object is a separate view model class:
DataContext = new SomeTypeOfViewModel();
Or like this if the property is declared in the code behind:
DataContext = this;
2) You must implement the INotifyPropertyChanged interface or implement DependencyPropertys - you seem to have implemented the INotifyPropertyChanged interface, but you must ensure that you have done it correctly.
3) You must provide a valid Binding Path... BINDING is not valid, so an appropriate Binding Path for you might be this (depending on where you have declared your property):
<m:Map x:Name="MyMap" Grid.Row="1" CredentialsProvider="KEY"
ZoomLevel="{Binding MapZoomLevel}" Mode="Road">
Please read the Data Binding Overview page on MSDN for the full story.
based on your tags you are using Caliburn Micro with this? Datacontext is already set with viewmodel/view from the framework. ZoomLevel="{Binding MapZoomLevel, Mode=TwoWay}" is required.
I have a RootViewModel class, and I want to access an UI element (instantialized in MainWindow) from there. For that I set the class this way:
class RootViewModel : MainWindow, INotifyPropertyChanged
But the application doesn't start. It compiles and throws no error but the Window doesn't appear. If I remove that MainWindow, I can't access my element that has been created in MainWindow.xaml. What can I do to solve this?
EDIT: Ok, I understand that I shouldn't be doing that, it's going against what it is MVVM. But is there a way to modify directly something from MainWindow? What should I try instead of this?
Consider changing RootViewModel to a UserControl. Give it a DependencyProperty called Element, of type UIElement.
Add RootViewModel to the XAML for MainWindow and bind to the element you want to use, like this;
<RootViewModel Element="{Binding ElementName=SourceElement}"/>
WPF windows are objects, so you can always instantiate them manually, like so:
var foo = new FooWindow(); // new Window object
foo.Show(); // show window as non-blocking "dialog"
If you do that, you have access to any public or protected members of the window - that includes any child controls, as long as their Accessibility properties are marked accordingly. So, if FooWindow had a TextBox named txtFooName, you could access it like so:
string name = foo.txtFooName.Text // get string value from textbox
You can also assign to any public/protected members:
foo.txtFooName.Text = "Fizz Buzz, Inc.";
Now, MainWindow is generally set as the StartupUri of the application (in App.xaml), which makes it the entry point for the application, so I'm not entirely sure what you're trying to do.
I was able to achieve what I wanted by creating a
public ObservableCollection<ChartPlotter> myPlotCollection { get; set; }
And then adding a ChartPlotter there, and setting in XAML:
<DockPanel Grid.Column="2">
<ItemsControl Width="Auto"
Height="Auto"
ItemsSource="{Binding myPlotCollection}">
</ItemsControl>
</DockPanel>
So this way I have complete control over what is happening in myPlotCollection[0]. At this moment it's enough for me, later I'll give it another try to bind it properly.
Greetings folks!
I'm running into a problem with WPF databinding that I hope you can help out with. I'm new to WPF but an expereienced developer (VB 3.0-6.0, C#).
Here's the scenario:
I have a C# project called MasterPartsData which contains a number of classes which reprsent different types of parts (capacitor, diode, etc). They inherit from a base class called clsPart.
I have another C# WPF project which contains WPF UserControls (as well as a MainWindow) to visually represent the values stored in an individual MasterPartsData (MPD) object. I've created a private field in the usercontrol to hold the object with a getter and setter.
If I create a binding explicitly in the setter for the populated object:
_capacitor = value;
Binding binding = new Binding();
binding.Source = _capacitor;
binding.Path = new PropertyPath("C0uf");
this.txtC0uf.SetBinding(TextBox.TextProperty, binding);
(with _capacitor being the private object variable and C0uf being the property name)
the value correctly displays.
However I don't wish to have to explicitly create each binding in the code behind. My preference is to create the bindings inline in XAML, perhaps with a DataContext pointing to the object.
Unfortunately every different permutation I've tried fails to work; the text box doesn't show data.
I have a couple of suspicions:
1) The binding is correct, but the text box needs to be refreshed.
2) The binding is confused between the private variable and the properties.
3) Maybe the fact that the class is defined in a different project is causing issues.
4) I'm going mad and should check myself into an asylum before someone gets hurt. :)
Any help you can provide would be most appreciated. I'm more than happy to add more information, but didn't want to clutter the question with pages and pages of source.
With respect to your suspicions:
1) I think the default binding behavior of a TextBox is TwoWay, with a LostFocus update trigger, meaning that your UI focus will have to change to another control before the binding will update, if changes are made in the UI.
If changes are made in the code you need to raise the NotifyPropertyChanged event in order for the binding system to see it.
2) This is probably not the case, but it leaves the impression that you're trying to set bindings on your UserControl properties, which is not the way data binding was designed to be used in this particular kind of use case. What you want is to bind data from non-UI classes to dependency properties on your UserControls.
3) This will never matter, as long as your UI project has a reference to your classes.
4) This is a common reaction people have when beginning to use XAML and WPF. It's like instead of being handed a box of Legos, you just got handed an injection molding machine with insufficient instructions, isn't it?
Overall, this is a situation where you might need to examine your design; elements of the "Model-View-ViewModel" pattern will come in handy. If you're unfamiliar with this, it's a development pattern in which you introduce a "ViewModel" class, perhaps you can call it MasterPartsVM which contains an implementation of INotifyPropertyChanged.
The DataContext of your UserControl would be set to this MasterPartsVM class.
A brief code example, using some generic names. Given a ViewModel class with a small backing class that looks like this:
class PartViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public PartClass Data { get; set; }
public String SomeVMProperty
{
get { return Data.SomeProperty; }
set
{
if (Data.SomeProperty != value)
Data.SomeProperty = value;
this.PropertyChanged(this, new PropertyChangedEventArgs("SomeVMProperty"));
}
}
}
class PartClass
{
public string SomeProperty { get; set; }
}
The XAML of a basic UserControl would look like this:
<UserControl x:Class="WpfApplication1.PartUserControl"
... >
<Grid>
<TextBox Text="{Binding SomeVMProperty}" Margin="68,77,104,176" />
</Grid>
</UserControl>
To connect your data class to this UserControl, you set the UserControl's DataContext property. If you do this in code, it's a matter of having a reference to your user control and the ViewModel, and then setting the property:
MyUserControlInstance.DataContext = new PartViewModel(); // or some existing PartViewModel
That combination of code should work to produce a textbox whose Text property changes every time the SomeVMProperty property is changed.
In a basic binding scenario, if your class looks like this
public class MasterPartsData
{
private string _c0uf;
public string C0uf
{
get { return _c0uf;}
set { _c0uf = value;}
}
public MasterPartsData()
{
C0uf = "Hello World!";
}
}
your XAML would look like this
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" >
<Window.DataContext>
<local:MasterPartsData />
</Window.DataContext>
<Grid>
<TextBlock Text="{Binding Path=C0uf}" />
</Grid>
</Window>
Note, there are many different approaches to setting the DataContext, you don't necessarily just have to do it in the XAML
Also, typically your MasterDataParts class would implement INotifyPropertyChanged