I am using the mahapps metro app to make a gui in wpf. I have used the code
<Controls:Badged Badge="{Binding Path=BadgeValue}">
<!-- Control to wrap goes here -->
<Button Content="Notifications" />
</Controls:Badged>
Say, if i want to update the 'BadgeValue' in the notifications callback, how do i go about doing this? plz help..
You set the BadgeValue source property that you bind to in your XAML and raise the PropertyChanged event, just like you would update any other data-bound property.
Here is an example for you:
View Model:
public class ViewModel : INotifyPropertyChanged
{
private string _badgeValue;
public string BadgeValue
{
get { return _badgeValue; }
set { _badgeValue = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
ViewModel viewModel = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = viewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
viewModel.BadgeValue = "new value...";
}
}
MainWindow.xaml:
<Controls:Badged Badge="{Binding Path=BadgeValue}">
<Button Content="Notifications" />
</Controls:Badged>
<Button Content="Update" Click="Button_Click" />
Related
I have following textbox binding:
XAML:
<TextBlock x:Name="Auslastungskapazität1" Text="{Binding Kapazität, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Foreground="Black" HorizontalAlignment="Center" Margin="0,5,5,5" FontSize="16" ></TextBlock>
MainViewModel Class:
class MainViewModel: ZuliefererStandortListe, IDropTarget, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Kapazität {
get { return _kapazität1Ausgelastet; }
set {
if (this._kapazität1Ausgelastet != value)
_kapazität1Ausgelastet = value;
OnPropertyChanged("Kapazität");
}
}
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
DataContext is the in the MainWindow Constructor as
Kapazität.DataContext = new MainViewModel();
If I change Kapazität the int get changed and the OnPropertyChanged() method gets called. However "PropertyChanged" remains null and therefore the Textbox Binding doesn't get updated.
Either set the DataContext of the TextBox itself:
Auslastungskapazität1.DataContext = new MainViewModel();
...or of any of its parent elements, such as for example the window:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
I have implemented something violating the MVVM pattern, and I wondered if there was a MVVM way of doing this.
I have a Window MainWindow, its DataContext is bound to a class called ViewModel which implements INotifyPropertyChanged.
I also implemented a Window ChildWindow which appears in a "Dialog" style when a button is clicked, using a RelayCommand. The DataContext of ChildWindow also binds to ViewModel. This Window is used to fill the details of a new list Item. I pass the View as a CommandParameter to the ViewModel, so that the ChildWindow can be centered in comparison to the MainWindow. This is not MVVM, and I would like to change this.
First, I implemented this in a non-MVVM way:
Here is my XAML for the button in MainWindow which opens the ChildWindow:
<Button Name="BtnInsert" Width="50" Margin="10" Command="{Binding OpenChildWindowCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">Add</Button>
Here is my simplified XAML for the ChildWindow:
<Window x:Class="HWE_Einteilen_Prototype.View.ListItemWindow"
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:HWE_Einteilen_Prototype.View"
mc:Ignorable="d"
Title="test" Height="400" Width="400">
<TextBox Width="50" Text="{Binding CurrentListItem.Id}" ></TextBox>
</Window>
And here is my (simplified) ViewModel Class:
public class ViewModel : INotifyPropertyChanged
{
private DataContext _ctx;
private ListItem _currentListItem;
private ObservableCollection<listItem> _listItems;
private ListItemWindow _listItemWindow;
private ICommand _openListItemWindowCommand;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<ListItem> ListItems
{
get { return _listItems; }
set
{
_listItems = value;
OnPropertyChanged();
}
}
public ListItem CurrentListItem
{
get { return _currentListItem; }
set
{
_currentListItem = value;
OnPropertyChanged();
}
}
public ICommand OpenListItemWindowCommand
{
get { return _openListItemWindowCommand; }
set
{
_openListItemWindowCommand = value;
OnPropertyChanged();
}
}
public ViewModel()
{
OpenListItemWindowCommand = new RelayCommand(this.OpenNewListItemWindow, this.CanOpenListItemWindow);
}
private void OpenNewListItemWindow(object parameter)
{
CurrentListItem = new listItem(){Id = "testId"};
_listItemWindow = new StListItemWindow(){DataContext = this};
_listItemWindow.Owner = (MainWindow)parameter;
_listItemWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
_listItemWindow.Closing += OnStListItemWindowClosing;
_listItemWindow.Show();
}
private bool CanOpenListItemWindow(object parameter)
{
return true;
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
What I have tried:
I have tried implementing a Behavior (from system.windows.interactivity) for the button opening the child window, so that it creates a new Window and does all the centering and owner stuff, and leaving only CurrentListItem = new listItem(){Id = "testId"}; in the command method. However, in this case binding to CurrentListItem in the ChildWindow throws an exception.
XAML Code for the MainWindow Button:
<Button Name="BtnInsert" Width="50" Margin="10" Command="{Binding OpenListItemWindowCommand}" Content="Add">
<i:Interaction.Behaviors>
<behaviors:BehButtonNewWindow></behaviors:BehButtonNewWindow>
</i:Interaction.Behaviors>
</Button>
Behavior Code:
class BehButtonNewWindow : Behavior<Button>
{
private StListItemWindow _ListItemWindow;
protected override void OnAttached()
{
AssociatedObject.Click += OnClickHandler;
}
protected override void OnDetaching()
{
AssociatedObject.Click -= OnClickHandler;
}
private void OnClickHandler(object sender, RoutedEventArgs routedEventArgs)
{
if (sender is Button button)
{
var win = Window.GetWindow(button);
if (win != null)
{
_ListItemWindow = new ListItemWindow
{
DataContext = win.DataContext,
Owner = win,
WindowStartupLocation = WindowStartupLocation.CenterOwner
};
_ListItemWindow.Show();
}
}
}
}
Code of Command Execute Method from ViewModel:
private void OpenNewStListItemWindow(object parameter)
{
CurrentListItem = new ListItem(){Id = "testId"};
}
What am I doing wrong?
Credit for this answer goes to Will (see comments)
On handling the window opening:
Opening a window is a UI concern. Simply handle the button click in the codebehind, construct a new window and stick the current VM in it. MVVM != no codebehind.
On handling vm code:
[...] If you mean that last little bit of code at the bottom, make it public and have the window call it before opening the new window. The UI is perfectly fine knowing about your view models. They're designed to display their state and bind to their properties.
Thanks for your help!
Okay I've been wracking my brain a lot about this one, I'm missing something, I just can't figure out what. Ultimately I'm trying to set databinding so I can update values to be shown on the fly, but for the life of me, it's not working.
The XAML is:
<TextBox x:Name="textBox" HorizontalAlignment="Left"
Height="37" Margin="85,38,0,0" TextWrapping="Wrap"
Text="{Binding Path=TBBind}" VerticalAlignment="Top"
Width="121" />
Note that I have the {Binding Path=TBBind} set.
The code behind is:
using System.ComponentModel;
using System.Windows;
namespace Databinding_Practice_2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
TBBind = "test";
}
private string _tBBind;
public string TBBind
{
get { return _tBBind; }
set
{
if (value != _tBBind)
{
_tBBind = value;
OnPropertyChanged("TBBind");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
MessageBox.Show("OnPropertyChanged triggered");
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
Help me obi-w.... oh wait, help me anyone!
Assuming that you are trying to use the MVVM pattern (which stands for Model-View-ViewModel):
Your MainWindow is the View.
You should create another class to be the View Model, like this:
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
TBBind = "test";
}
private string _tBBind;
public string TBBind
{
get { return _tBBind; }
set
{
if (value != _tBBind)
{
_tBBind = value;
OnPropertyChanged("TBBind");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
MessageBox.Show("OnPropertyChanged triggered");
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
Your MainWindow code behind will become like this after removing all ViewModel related stuff to the MainWindowViewModel class:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
Now, you should link the View with the ViewModel, there are many ways to do this. Here is one of them:
In the XAML of MainWindow, have the following inside the Window element:
<Window.DataContext>
<wpfApplication5:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="37" Margin="85,38,0,0" TextWrapping="Wrap" Text="{Binding TBBind}" VerticalAlignment="Top" Width="121" />
</Grid>
Please note that WpfApplication5 is the name of the namespace in my WPF project. This will probably be different in your case.
Try:
public MainWindow()
{
InitializeComponent();
DataContext = this;
TBBind = "test";
}
The difference here sets the critical DataContext property. This is the cornerstone of the MVVM pattern, which you are implementing here. You should consider separating the View Model responsibility into another class, and then setting the View's DataContext to an instance of that class, but the approach you have taken here works for simple cases.
I have a TextBlock control inside a HubSection in a Windows 8.1 Universal app.
<TextBlock x:Name="api_enabled_label"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Text="{Binding APIinfotext}" />
Now when the page is launched, in the contrustor, there is a method that is run.
public string APIinfotext { get; set; }
public sealed partial class MainPage : Page {
VoipMS voip_service = new VoipMS("shoukatali#hotmail.com", "Kitt0cat");
public string APIinfotext { get; set; }
public MainPage() {
this.InitializeComponent();
// disable sections until API is enabled
mainpagehub.Sections[1].IsEnabled = false;
mainpagehub.Sections[2].IsEnabled = false;
//check for API and enable sections
checkAPI();
}
private async void checkAPI() {
//irrelevant code above
switch (result) {
case "success":
APIinfotext = "Your API is connected";
break;
//irrelevant code below
}
}
So why dosnt this work? I set the DataContext of the Textblock to the current class (which is the MainPage partial class) and the property is a public property.
Note: Today is my first time working with .net 4.5 with XAML after a huge break at the .net 2.0 framework with WinForms.
Your binding doesn't know that APIinfotext property has changed. To let the bindings know that the property has changed you can do one of the following. The first one is the easiest.
1) implement INotifyPropertyChanged interface and raise the PropertyChanged changed event once APIinfotext has changed (PropertyChanged("APIinfotext"));
2) Have an event called APIinfotextChanged with the standard event signature and raise that event after the property has changed.
3) Implement your property as a DependencyProperty (not an ideal solution in this case).
You might be missing the part where you have to RaiseProperyChange NotifyPropertyChage to update the bindings. your Model should implement the INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
then
RaisePropertyChanged("APIinfotext");
http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.propertychanged.aspx
Looks like you need a very simple example of what the other two are talking about. Let's assume nothing. You need to set the DataContext correctly, plus raise the event. This is as simple as I can put it, when you click on the button it will change the TextBox because I change the Property which raises the event.
XAML
<Page>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBox Text="{Binding APIinfotext}" Height="100" Width="400" HorizontalAlignment="Left"/>
<Button x:Name="myButton" Content="Change Text" Height="200" Width="400" Click="myButton_Click"/>
</StackPanel>
</Grid>
</Page>
C# (Pay attention, to the SET part of the APIinfotext)
using System.ComponentModel; // INotifyPropertyChanged
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
private string _apiinfotext = "Default Text";
public string APIinfotext
{
get { return _apiinfotext; }
set
{
_apiinfotext = value;
RaisePropertyChanged("APIinfotext");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public MainPage()
{
this.InitializeComponent();
this.DataContext = this;
}
private void myButton_Click(object sender, RoutedEventArgs e)
{
this.APIinfotext = "Don't confuse movement for progress.";
}
}
In my user control I have this property:
public static DependencyProperty FooListProperty = DependencyProperty.Register(
"FooList", typeof(List<Problem>), typeof(ProblemView));
public List<Problem> FooList
{
get
{
return (List<Problem>)GetValue(FooListProperty);
}
set
{
SetValue(FooListProperty, value);
}
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == FooListProperty)
{
// Do something
}
}
And since another window, I´m trying to set a value for the last user control:
<local:ProblemView HorizontalAlignment="Center"
VerticalAlignment="Center" FooList="{Binding list}" />
And that window in load contains:
public List<Problem> list;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Some processes and it sets to list field
list = a;
}
But in XAML code, binding it isn't working. Don't pass the data. What am I wrong?
You can't bind to a Field in WPF, you'll have to change list to a property instead.
You call the Dependency Property FooList in your UserControl and ResultList in Xaml but I'm guessing that's a typo in the question.
You should implement INotifyPropertyChanged in the Window to let the Bindings know that the value has been updated.
I'm not sure if you have the correct DataContext set in the Xaml ProblemView, if you're unsure you can name the Window and use ElementName in the binding
<Window Name="window"
...>
<!--...-->
<local:ProblemView HorizontalAlignment="Center"
VerticalAlignment="Center"
ResultList="{Binding ElementName=window,
Path=List}" />
<!--...-->
</Window>
Sample code behind
public partial class MainWindow : Window, INotifyPropertyChanged
{
//...
private List<Problem> m_list;
public List<Problem> List
{
get { return m_list; }
set
{
m_list = value;
OnPropertyChanged("List");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}