WPF: bind property from MainWindow.xaml cause an error - wpf

So I have this view model:
public class WiresharkFiles : INotifyPropertyChanged
{
public ObservableCollection<WiresharkFile> List { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private bool _inUse;
private int _packets;
private bool _hasItems;
public WiresharkFiles()
{
List = new ObservableCollection<WiresharkFile>();
HasItems = false;
List.CollectionChanged += List_CollectionChanged;
}
private void List_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
HasItems = List.Count > 0;
}
public bool InUse
{
get { return _inUse; }
set
{
_inUse = value;
NotifyPropertyChanged("InUse");
}
}
public int Packets
{
get { return _packets; }
set
{
_packets = value;
NotifyPropertyChanged("Packets");
}
}
public bool HasItems
{
get { return _hasItems; }
set
{
_hasItems = value;
NotifyPropertyChanged("HasItems");
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml
private WiresharkFiles caps;
public MainWindow()
{
InitializeComponent();
caps = new WiresharkFiles();
}
Window.Resources
<Window.Resources>
<Convertors:CollectionHasItemsConverter x:Key="CollectionHasItemsConverter"/>
</Window.Resources>
Converter
public class CollectionHasItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And base of my collection item (empty or not) i want to enable/disable my Button:
<Button Name="btnDeleteAll"
Click="btnDeleteAll_Click"
IsEnabled="{Binding Path=(caps.HasItems),Converter={StaticResource CollectionHasItemsConverter}}">
And i got this error:
XamlParseException: Type reference cannot find type named
'{http://schemas.microsoft.com/winfx/2006/xaml/presentation}caps'.

I don't see where you're associating your DataContext with the caps property.
Make sure you have a public property because the WPF engine isn't running from within your class and won't be able to access the private WiresharkFiles caps; variable. Try the following:
private WiresharkFiles caps;
public WiresharkFiles Files { get { return caps; } }
with a corresponding
public MainWindow()
{
InitializeComponent();
caps = new WiresharkFiles();
DataContext = Files;
}
Your XAML will then bind to Files as follows
IsEnabled="{Binding Path=HasItems}"
Update You'll need to have a look at implementing and binding to commands for the button which will make it a lot better. Look at this article for info on implementing and dealing with commands.

caps is a private variable:
private WiresharkFiles caps;
In order to bind, it would have to be a public property:
public WiresharkFiles caps {get;set;}
You would also have to set the datacontext of the window to itself. Something like:
this.DataContext = this;
or
In your window tag put:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
I don't see how this relates to your initial question but you can use dot notation in binding.
You can bind:
{Binding AnObservableCollection.Count}
And you can compare that to 0 in a datatrigger. With a button and a bound command if you want to disable it then I'd use the canexecute of icommand and return false if you have no entries or whatever your logic is.

Related

How to determine whether MediaElement is playing?

I want to determine video is_playing on my player .
But how??
Please help me.
I am using c#>w_pf .net framework 4.5.1
I want to create a event like this video.
See Photo Demo
I want to control play/pause button like that player.
How can I control play/pause button image change while video is playing.
Please help me
i did this by having 2 buttons and binding the visibility of those buttons to a bool that you control when your buttons are clicked.
basically you must keep track of the play pause state yourself. media element wont do this for you.
in main window xaml add the following:
// Two converters used with the visibility binding
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<local:InvertableBooleanToVisibilityConverter x:Key="InvertableBooleanToVisibilityConverter"/>
</Window.Resources>
// Need to make sure we add loaded behaviour or it will crash app
<MediaElement Name="mePlayer"
LoadedBehavior="Manual"
Source="C:\Users\SamG\Downloads\ZW_20160102_064536A.avi"/>
<Button Content="Play" Name="Play" Click="Play_OnClick" Visibility="{Binding ElementName=mainWindow, Path=IsPaused, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<Button Content="Pause" Name="Pause" Click="Pause_OnClick" Visibility="{Binding ElementName=mainWindow, Path=IsPaused, Converter={StaticResource InvertableBooleanToVisibilityConverter}, ConverterParameter=Inverted}"/>
you must also name this window make sure you put this inside
x:Name="mainWindow"
in the c# code:
public partial class MainWindow : INotifyPropertyChanged
{
private bool _isPaused;
public MainWindow()
{
InitializeComponent();
mePlayer.Play();
IsPaused = true;
}
private void Play_OnClick(object sender, RoutedEventArgs e)
{
mePlayer.Play();
IsPaused = false;
}
private void Pause_OnClick(object sender, RoutedEventArgs e)
{
mePlayer.Pause();
IsPaused = true;
}
public bool IsPaused
{
get { return _isPaused; }
set
{
_isPaused = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
then add a new class called InvertableBooleanToVisibilityConverter:
[ValueConversion(typeof(bool), typeof(Visibility))]
public class InvertableBooleanToVisibilityConverter : IValueConverter
{
enum Parameters
{
Normal, Inverted
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var boolValue = (bool) value;
var direction = (Parameters) Enum.Parse(typeof (Parameters), (string) parameter);
if (direction == Parameters.Inverted)
return !boolValue ? Visibility.Visible : Visibility.Collapsed;
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
copy this code it should work for what you need

Bind to a property and Element value - WPF binding

I have a checkbox which is binded to a object's property "IsValidCustomer" and I have a listview that holds some customers.
Whenever My user selects any Customer in the list, I want the Checkbox Checked property to set to False that means my "IsValidCustomer" property also will set to False automatically. Is there any way of achieving this using WPF bindings?
Any help in this regard would be highly appriciated.
Regards
-Srikanth
First make sure that your view's Datacontext is set to a viewmodel that implements the INotifyPropertyChanged interface then add a SelectedCustomer property that will hold the selected Customer from the ListView,
Each time the SelectedCustomer is set, check its value and set the IsValidCustomer property
here the full code :
the View model
public class Customer
{
public String Name { get; set; }
public String Id { get; set; }
}
public partial class MainWindow : Window, INotifyPropertyChanged
{
private Customer _selectedCustomer;
public Customer SelectedCustomer
{
get
{
return _selectedCustomer;
}
set
{
if (_selectedCustomer == value)
{
return;
}
_selectedCustomer = value;
OnPropertyChanged();
IsValidCustomer = (_selectedCustomer == null);
}
}
private ObservableCollection<Customer> _listCustomers;
public ObservableCollection<Customer> ListCustomers
{
get
{
return _listCustomers;
}
set
{
if (_listCustomers == value)
{
return;
}
_listCustomers = value;
OnPropertyChanged();
}
}
private bool _isValidCustomer = false;
public bool IsValidCustomer
{
get
{
return _isValidCustomer;
}
set
{
if (_isValidCustomer == value)
{
return;
}
_isValidCustomer = value;
OnPropertyChanged();
}
}
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
and the view
<StackPanel>
<CheckBox Content="IsValidCustomer" IsChecked="{Binding IsValidCustomer,Mode=TwoWay}"></CheckBox>
<ListView ItemsSource="{Binding ListCustomers}" SelectedItem="{Binding SelectedCustomer,Mode=TwoWay}"></ListView>
</StackPanel>
I am sure you have something like this in your model :
private bool _IsValidCustomer;
public bool IsValidCustomer
{
get { return _IsValidCustomer; }
set
{
_IsValidCustomer= value;
PropertyChanged(this, new PropertyChangedEventArgs("IsValidCustomer"));
}
}
Set the Binding for that bool property.
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding IsValidCustomer, Converter={StaticResource InverseBooleanConverter}}"/>
</Style>
Your CheckBox will be bound to this also :
<CheckBox IsChecked="{Binding IsValidCustomer, Mode=TwoWay}"/>
So, i assume you start with that IsValidCustomer set to true. And on selecting each row, you want to set it to false.
You will need an inverse boolean converter for this:
public class InverseBooleanConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}

Binding Fails Silently for Nested Property

I am trying to bind a dependency property to an INotifyPropertyChanged-enabled property with a multi-level property path.
When the owner object of the property is not null, the binding works, but if the owner object is null, the binding does not do anything (the converter is not called and TargetNullValue is not used).
Here is some minimal sample code to reproduce the problem:
Window1.xaml.cs:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel;
namespace NPCPropertyPath
{
public abstract class VMBase : INotifyPropertyChanged
{
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (e == null) {
throw new ArgumentNullException("e");
}
if (PropertyChanged != null) {
PropertyChanged(this, e);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Window1 : Window
{
private class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) {
return null;
} else if (value is int) {
return (((int)value) + 15).ToString();
} else {
return "no int";
}
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
private class InnerVM : VMBase
{
private int myValue;
public int MyValue {
get {
return myValue;
}
set {
if (myValue != value) {
myValue = value;
OnPropertyChanged("MyValue");
}
}
}
}
private class OuterVM : VMBase
{
private InnerVM thing;
public InnerVM Thing {
get {
return thing;
}
set {
if (thing != value) {
thing = value;
OnPropertyChanged("Thing");
}
}
}
}
private readonly OuterVM vm = new OuterVM();
public Window1()
{
InitializeComponent();
var txt = new TextBlock();
txt.SetBinding(TextBlock.TextProperty,
new Binding("Thing.MyValue") {
Source = vm,
Mode = BindingMode.OneWay,
Converter = new MyConverter(),
TargetNullValue = "(error)"
});
container.Content = txt;
var txt2 = new TextBlock();
txt2.SetBinding(TextBlock.TextProperty,
new Binding("Thing") {
Source = vm,
Mode = BindingMode.OneWay,
Converter = new MyConverter(),
TargetNullValue = "(error)"
});
container2.Content = txt2;
}
void Button_Click(object sender, RoutedEventArgs e)
{
vm.Thing = new InnerVM();
vm.Thing.MyValue += 10;
}
}
}
Window1.xaml:
<Window x:Class="NPCPropertyPath.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NPCPropertyPath" Height="300" Width="300">
<StackPanel>
<Button Content="Change value" Click="Button_Click"/>
<ContentControl Name="container"/>
<ContentControl Name="container2"/>
</StackPanel>
</Window>
Of course, this is a significantly simplified form of my real application, where there is quite a bit more going on and the involved classes are not crammed together in two files (or even in the same assembly) where they can all see each other.
This sample displays a window with a button and two content controls. Each of the content controls contains a TextBlock whose Text property is bound to a property from a view-model. The view-model instance (of type OuterVM) is assigned to the Source property of each binding.
OuterVM implements INotifyPropertyChanged; it has a property Thing of type InnerVM (which also implements INotifyPropertyChanged), which in turn has a property MyValue.
The first text block is bound to Thing.MyValue, the second one just to Thing. Both of the bindings have a converter set, as well as a value for the TargetNullValue property that should be displayed on the text block if the target property is null.
The Thing property of the OuterVM instance is initially null. When clicking the button, something is assigned to that property.
The problem: Only the second text block displays anything initially. The one that is bound to Thing.MyValue neither invokes the converter (as evidenced by setting breakpoints), nor does it use the value of the TargetNullValue property.
Why? And how can I have the first text block display a default value instead of Thing.MyValue while Thing is not assigned?
For this purpose you should not use TargetNullValue, you should use FallbackValue property of the Binding.

Cleaning up automatically created View Model

Currently I'm binding my View Control straight to my Model.
But I'd like to :
a) provide more events/properties than the ones exposed in the Model.
b) provide properties based on Model data that are more suited for the View
And so to do this, I've decided to introduce a layer in between, which I'm calling the ViewModel(not sure if this is the correct use for the term ViewModel)
The work of the ViewModel in my scenario is to subscribe to all the events exposed by the Model and use those events to modify dependency properties in the ViewModel.
I've done this as follows.
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:View DataContext="{Binding Converter={StaticResource modelToViewModel}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here, Items is a collection of 'Model' type data, which I convert to 'ViewModel' types that the view can then bind to.
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
int m_age;
public int Age
{
get { return m_age; }
set { m_age = value; NotifyPropertyChanged("Age"); }
}
void NotifyPropertyChanged(string _property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(_property));
}
}
}
public class ViewModel : DependencyObject
{
public Model Model { get; private set; }
public ViewModel(Model _model)
{
Model = _model;
Model.PropertyChanged += OnModelPropertyChanged;
}
void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// .. here - I would modify this ViewModels dependency properties
}
}
public class ModelToViewModel : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new ViewModel(value as Model);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The problem I have now, is how to dispose of the ViewModel so it can unregister the property changed events in ViewModel. I'm thinking the best place to do this would be in the unloaded event of the View, but would like your thoughts.
public partial class View : UserControl
{
public View()
{
Unloaded += OnUnloaded;
InitializeComponent();
}
void OnUnloaded(object sender, RoutedEventArgs e)
{
if (DataContext != null)
{
(DataContext as ViewModel).Dispose();
}
}
}
Edit : I guess I would also need to Call dispose when the datacontext of the View changes, which would occur if an Item in the Items list was replaced.
Well you could implement IDisposible.
public class ViewModel : DependencyObject, IDisposable
{
public Model Model { get; private set; }
public ViewModel(Model _model)
{
Model = _model;
Model.PropertyChanged += OnModelPropertyChanged;
}
~ViewModel()
{
Dispose(false);
}
void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// .. here - I would modify this ViewModels dependency properties
}
public void Dispose()
{
Dispose(true);
GC.SupressFinalize();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (Model != null)
Model.PropertyChanged -= OnModelPropertyChanged;
}
}
}
If you're wondering about the implementation. You can find some information here. You must also unassign the call back in the view.
public partial class View : UserControl
{
public View()
{
Unloaded += OnUnloaded;
InitializeComponent();
}
void OnUnloaded(object sender, RoutedEventArgs e)
{
if (DataContext != null)
{
(DataContext as ViewModel).Dispose();
}
Unloaded -= OnUnloaded; // <---
}
}

What's wrong with my binding?

I'm working on an application using a TCP protocol.
I want to show the different statuses of the communication in different colors (connect = green, disconnect = red)
I defined an enum:
public enum ComunicationStateTypeEnum
{
COMUNICATION_CONNECTED,
COMUNICATION_DISCONNECTED
};
I defined a conversion class:
namespace Conversion
{
[ValueConversion(typeof(ComunicationStateTypeEnum), typeof(Brushes))]
public class ComStatusToColor : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ComunicationStateTypeEnum state = (ComunicationStateTypeEnum)value;
if (state == ComunicationStateTypeEnum.COMUNICATION_CONNECTED)
return Brushes.Green;
return Brushes.Red;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
In Xaml I defined an ellipse:
<Ellipse Name="ComEllipse" Height="25" Width="30" Fill ="{Binding Path=eCommStatus, Converter={StaticResource ComStatusToColor}}" Stroke="Black" DockPanel.Dock="Left"/>
also in Xaml I defined:
xmlns:ConversionNamespace="clr-namespace:Conversion"
<Window.Resources>
<ConversionNamespace:ComStatusToColor x:Key="ComStatusToColor"/>
</Window.Resources>
I want to bind to an existing object, therefore I initiated:
ComEllipse.DataContext = SystemLogic.GetInstance();
(SystemLogic is a singleton)
and in SystemLogic I defined:
public class SystemLogic
{
public ComunicationStateTypeEnum eCommStatus { get; set; }
...
}
eCommStatus is initiated to COMUNICATION_DISCONNECTED in the constructor and the ellipse turns red, and still when eCommStatus member changes to COMUNICATION_CONNECTED , the ellipse doesn't change its color
What's wrong?
Gil
You need to implement INotifyPropertyChanged interface in your SystemLogic class to let UI know that a property's value has changed. This way UI can update itself.
Example
public class SystemLogic : INotifyPropertyChanged
{
private ComunicationStateTypeEnum _eCommStatus;
public ComunicationStateTypeEnum eCommStatus
{
get
{
return _eCommStatus;
}
set
{
_eCommStatus = value;
RaisePropertyChanged("eCommStatus");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(String propertyName)
{
PropertyChangedEventHandler temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Nothing wrong with your binding. But SystemLogic must implement INotifyPropertyChanged to report back when it is changed.
public class SystemLogic
{
private ComunicationStateTypeEnum _eCommStatus;
public ComunicationStateTypeEnum eCommStatus
{
get{return _eCommStatus;}
set
{
_eCommStatus = value;
OnPropertyChanged("ComunicationStateTypeEnum");
}
}
...
}

Resources