I used the solution located at stackoverflow:event-fired-when-item-is-added-to-listview to utilize interface INotifyCollectionChanged in CodeBehind. Is there a way to add this EventHandler within the XAML?
Essentially, I want this line defined in XML:
((INotifyCollectionChanged)lbFiles.Items).CollectionChanged += lbFiles_SelectionChanged;
You should just be creating the CollectionChanged event on the collection that is bound to your ListBox/ListView etc, accessing controls derictly from code behind is not the WPF way to do things.
Example:
Xaml:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="340" Width="480" Name="UI" >
<Grid DataContext="{Binding ElementName=UI}">
<ListBox ItemsSource="{Binding MyProperty}" />
</Grid>
</Window>
Code:
public partial class MainWindow : Window
{
private ObservableCollection<string> _myProperty = new ObservableCollection<string>();
public MainWindow()
{
InitializeComponent();
MyProperty.CollectionChanged += MyProperty_CollectionChanged;
}
public ObservableCollection<string> MyProperty
{
get { return _myProperty; }
set { _myProperty = value; }
}
void MyProperty_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// Collection Changed
}
}
Related
I am binding a RadioButton visibility using BoolToVisConverter.
I put this in xaml file:
xmlns:VM="clr-namespace:ScreenS.ViewModel"
<Window.DataContext>
<VM:MainViewModel />
</Window.DataContext>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</Window.Resources>
<RadioButton x:Name="SCB0" Visibility="{Binding ShowSCB0, Converter={StaticResource BoolToVisConverter}, FallbackValue=Hidden}" />
In the MainViewModel file, I enter:
using System.ComponentModel;
namespace ScreenS.ViewModel
{
public class MainViewModel : INotifyPropertyChanged
{
private bool _scb0;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public bool ShowSCB0
{
get { return _scb0; }
set
{
_scb0 = value;
NotifyPropertyChanged("ShowSCB0");
}
}
}
Finally, in the MainWindow file, I set:
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
MainViewModel mainView => DataContext as MainViewModel;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
mainView.ShowSCB0 = true;
}
Up to here, all works very well.
Problem is when I try to change this value from another class.
I am using:
class abc
{
MainViewModel viewModel = new MainViewModel();
public void someFunction()
{
viewModel.ShowSCB0 = true;
}
This does not set the visibility..
I am getting a bit lost, where am i going wrong?
You have to pay attention the way you instantiate your view models, especially when they are shared. Right now all depending types use their own instance of MainViewModel (or different references). That's why modifying the value of one instance is not reflected on the other instance.
Make use of ResourceDictionary. Consider to make the MainViewModel globally accessible by creating a shared instance inside App.xaml resources.
App.xaml
<Application ... >
<Application.Resources>
<ResourceDictionary>
<VM:MainViewModel x:Key="SharedMainViewModel" />
</ResourceDictionary>
</Application.Resources>
</Application>
MainWindow.xaml
<Window.DataContext>
<StaticResource ResourceKey="SharedMainViewModel" />
</Window.DataContext>
MainWindow.xaml.cs (fixed constructor)
public MainWindow()
{
InitializeComponent();
// The DataContext is initialized via XAML
}
Abc.cs
class Abc
{
private MainViewModel mainViewModel;
public Abc()
{
this.mainViewModel = Application.Current.Resources["SharedMainViewModel"] as MainViewModel;
}
}
I have the following UserControl:
<UserControl x:Class="DueVCheck.Pl.Gui.CustomControl.ListInfoBar"
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:DueVCheck.Pl.Gui.CustomControl"
mc:Ignorable="d" d:DataContext="{d:DesignInstance local:ListInfoBar}" >
<Button Name="NewRowBtn" Margin="5" Click="NewRowBtn_OnClick" Content="New"/>
</UserControl>
The Code-Behind file:
using System.Windows;
namespace DueVCheck.Pl.Gui.CustomControl
{
public partial class ListInfoBar
{
public ListInfoBar()
{
DataContext = this;
InitializeComponent();
}
public static readonly RoutedEvent NewClickEvent =
EventManager.RegisterRoutedEvent("NewClick", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(ListInfoBar));
public event RoutedEventHandler NewClick
{
add { AddHandler(NewClickEvent, value); }
remove { RemoveHandler(NewClickEvent, value); }
}
private void NewRowBtn_OnClick(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(NewClickEvent)); }
}
}
}
Usage of the Usercontrol:
<customControl:ListInfoBar Margin="10,0,10,0" NewClick="NewRowOnClick"/>
What i need is that the Usercontrol disables the NewRowBtn whenever the NewClick is not bound via XAML. The Problem is that Add or Remove get never called when binding the Event over XAML.
How to fix this?
I am coding an application, its a quiz, I have a main Window where I load different UserControls (Pages). so my problem is that I have one image on the MainWindow, I want to change the Visibility of this image from Collapsed to Visible from one of the UserControls but with no luck...
Here is my MainWindow:
<Window x:Class="MuseonQuiz_v3.PageSwitcher"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:pages="clr-namespace:MuseonQuiz_v3.Pages"
xmlns:k="http://schemas.microsoft.com/kinect/2013"
Title="MainWindow" Height="710" Width="1127" IsEnabled="True" DataContext="{Binding}" FontFamily="KaiTi" ResizeMode="NoResize" WindowStyle="None"
WindowStartupLocation="CenterScreen" WindowState="Maximized">
<Grid>
<Grid>
<k:KinectRegion Name="kinectRegion">
<ContentControl x:Name="mainContentControl"/>
</k:KinectRegion>
</Grid>
<Grid>
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
</Grid.Resources>
<k:KinectSensorChooserUI HorizontalAlignment="Center" VerticalAlignment="Top" Name="sensorChooserUi" />
<k:KinectUserViewer VerticalAlignment="Bottom" HorizontalAlignment="Center" k:KinectRegion.KinectRegion="{Binding ElementName=kinectRegion}" Height="600" Width="600" />
<Image Name="colorStreamImage" Width="640" Height="480" Visibility="Collapsed" HorizontalAlignment="Center" />
</Grid>
</Grid>
and this is my UserControl:
public partial class Selectie : UserControl, ISwitchable
{
string backgroundSelectie = "pack://application:,,,/MuseonQuiz_v3;component/Images/Selectie/selectie_background.jpg";
public Selectie()
{
InitializeComponent();
selectieBackground();
animatieButtons();
}
#region ISwitchable Members
public void UtilizeState(object state)
{
throw new NotImplementedException();
}
#endregion
}
My question is... how do I change the Visibility of the colorStreamImage that is located in the MainWindow from the UserControl... I have tried making an instance of the MainWindow, but that does not work, maybe I have to use some binding, but I am not sure, I appreciate any help you can provide!
As Clemens mentioned, your best bet is to go down the MVVM path. This is a good tutorial to get started In the Box – MVVM Training.
First, you can create a view model that implements INotifyPropertyChanged. In this case, you may want it to have at least one property of type Visibility.
public class MainViewModel : INotifyPropertyChanged
{
private Visibility _imageVisibility;
public Visibility ImageVisibility
{
get { return _imageVisibility; }
set { _imageVisibility = value; OnPropertyChanged("ImageVisibility"); }
}
private BitmapImage _imageSource;
public BitmapImage ImageSource{...}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler eventHandler = PropertyChanged;
if (eventHandler != null)
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
Now you'll want to set this view model as the data context on the main window. To do this, Paul Stovell has a good post on the different approaches: http://paulstovell.com/blog/mvvm-instantiation-approaches. Once we set it on the main window, the Selectie element will inherit the data context. Using the simplest approach:
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
Your Image element might then bind to the property like this:
<Image Visibility="{Binding ImageVisibility, UpdateSourceTrigger=PropertyChanged}" Source="{Binding ImageSource}" Height="200" Width="200"></Image>
The Selectie element can now change the ImageVisbility property on the view model since it shares the same data context as MainWindow. (I used the code-behind as an example. You'll probably want to push as much of that logic out of the view and into the view model or further downstream)
public partial class Selectie : UserControl
{
public Selectie()
{
InitializeComponent();
}
private void Selectie_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (((MainViewModel)this.DataContext).ImageVisibility == System.Windows.Visibility.Visible)
((MainViewModel)this.DataContext).ImageVisibility = System.Windows.Visibility.Collapsed;
else
((MainViewModel)this.DataContext).ImageVisibility = System.Windows.Visibility.Visible;
}
}
I have a user control "CtrlComments", this control has the following XAML (It's super basic).
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:wpftoolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
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"
x:Name="ucRoot">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="ID: " />
<TextBlock Text="{Binding Path=Deployment.Id}" />
</StackPanel>
</Grid>
The code behind is as follows, it's the bare basics to get the control to function. The key is the DependencyObject typeof(DeploymentDto) which has an int property called Id that we are interested in showing on our window as per XAML binding above.
public partial class CtrlComments : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty DeploymentProperty =
DependencyProperty.Register("Deployment", typeof(DeploymentDto),
typeof(CtrlComments), new PropertyMetadata(new DeploymentDto()));
public DeploymentDto Deployment
{
get
{
return (DeploymentDto)GetValue(DeploymentProperty);
}
set
{
SetValue(DeploymentProperty, value);
OnPropertyChanged(new PropertyChangedEventArgs("Deployment"));
}
}
public CtrlComments()
{
InitializeComponent();
this.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
Our problem is, despite the fact that the binding between the parent control and my user control via the dependency property is working (verified) and the OnPropertyChanged method firing, the TextBlock in my XAML isn't updating.
I have noticed that when the OnPropertyChanged method is run, the eventhandler is null meaning no one is notified that there was a property change.
I don't understand why this is the case though. If you could help explain where we are going wrong it would be enormously appreciated.
Thanks!
I have tried to replicate your problem and while doing so, I figured that the problem for me was in the following line in CtrlComments:
this.DataContext = this;
Dropping this line just made it work for me. Also note (as #Aron wrote in the comments) that the OnPropertyChanged of INotifyPropertyChanged shouldn't be called while in the setter of the DependencyProperty. At least for me it isn't necessary to implement INPC at all.
In the XAML file where you are using the UserControl you are most likely going to have another DataContext set (on a higher level, perhaps in the Window), and thus I guess it isn't inherited to the user control if already set in there (or overwritten). Below is my working code, but perhaps I misunderstood exactly what you're doing. If that is the case, please extend your question to include how you are using the UserControl, as that is a key to answering the question if this doesn't work :)
CtrlComments.xaml:
<UserControl x:Class="WpfApplication1.CtrlComments"
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>
<StackPanel Orientation="Horizontal">
<TextBlock Text="ID: "/>
<TextBlock Text="{Binding Path=Deployment.Id}"/>
</StackPanel>
</Grid>
</UserControl>
CtrlComments.xaml.cs:
namespace WpfApplication1
{
public partial class CtrlComments : UserControl
{
public static readonly DependencyProperty DeploymentProperty =
DependencyProperty.Register("Deployment", typeof(DeploymentDto), typeof(CtrlComments), new PropertyMetadata(new DeploymentDto { Id = 5 }));
public DeploymentDto Deployment
{
get { return (DeploymentDto)GetValue(DeploymentProperty); }
set
{
SetValue(DeploymentProperty, value);
}
}
public CtrlComments()
{
InitializeComponent();
}
}
}
MainWindow.xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:local="clr-namespace:WpfApplication1"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<local:CtrlComments x:Name="testUC" Height="100" Deployment="{Binding Deployment}"/>
<Button Click="Button_Click" Height="50" Width="100"/>
</StackPanel>
</Window>
MainWindow.xaml.cs:
namespace WpfApplication1
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
}
private DeploymentDto deployment = new DeploymentDto { Id = 2 };
public DeploymentDto Deployment
{
get { return deployment; }
set { deployment = value; OnPropertyChanged("Deployment"); }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Deployment = new DeploymentDto { Id = new Random().Next(100) };
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
}
DeploymentDto:
public class DeploymentDto
{
public int Id { get; set; }
}
It's quite ugly to bind MainWindow.DataContext to its code-behind, but since it's just used for example purposes I hope it's okay :)
I know a way to do MVC binding of one string to one TextBox. That's how it can be done:
C#:
namespace WpfApplication4
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = Model;
}
public ModelClass Model = new ModelClass();
private void button1_Click(object sender, RoutedEventArgs e)
{
Model.Output += "Setting New Output! ";
}
public class ModelClass : INotifyPropertyChanged
{
string _output;
public event PropertyChangedEventHandler PropertyChanged =
delegate { };
public string Output
{
get { return _output; }
set { _output = value;
PropertyChanged(this,
new PropertyChangedEventArgs("Output"));
}
}
}
}
}
XAML:
<Window x:Class="WpfApplication4.MainWindow"
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>
<Button Content="Button" VerticalAlignment="Top"
Name="button1" Click="button1_Click" />
<TextBox VerticalAlignment="Bottom"
Name="textBox1" Text="{Binding Path=Output}" />
</Grid>
</Window>
But I can't find a way to bind a two-dimensional array (or List) to a Grid or DataGrid. Can you help me with it? I couldn't find a working example on SO.
Consider using a DataGrid to display your two-dimensional array, assuming you can store your data as a List<ColumnData> where ColumnData is a class with one property per table column.
The WPF SDK contains a DataGrid, and there are several data grids from vendors available that have additional features.
if you wanna bind data to a datagrid you should be read something about the following.
ICollectionView, BindingListCollectionView
if you have somekind of collection you simply set the itemssource.
<DataGrid ItemsSource="{Binding Path=MyCollection, Mode=OneWay}" />
Collection types are mostly ObservableCollection or DataSet/DataTable. if your collection supports editing and so on, you can do it with the datagrid.