I'm creating app in UWP and i have question.
Can I somehow connection MVVM Light with SelectionChanged event (e.g. ListView) or with other event?
I would like that when I will click on some Item in ListView then I call SelectionChanged.
How do I do?
You can write the Method in ViewModel ,and use the x:bind to connection ViewModel.
The MVVMLight's method is use in WPF that cant bind the event in Method.
UWP can use x:bind to bind the UI event to ViewModel.
The sample:
XAML:
<ListView SelectionChanged = "{x:bind view.SelectionChanged }"/>
XAML.cs:
private ViewModel View{set;get;}
ViewModel:
public void SelectionChanged()
{
}
You can use ItemClick event that will run when you click the ListViewItem .
Inside your viewmodel something *.cs
public class RelayCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
this._canExecute = canExecute;
this._execute = execute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
public class MyViewModel
{
private ICommand _doSelectionChangedCommand;
public ICommand DoSelectionChangedCommand
{
get
{
if (_doSelectionChangedCommand == null)
{
_doSelectionChangedCommand = new RelayCommand(
p => this.CanSelectionChanged,
p => this.DoSomeImportantMethod());
}
return _doSomething;
}
}
}
In your viewSomemthing.xaml
--For namespace
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
--Then go down to your control, we'll use Combobox as an example
<ComboBox ... />
<i:Interaction.Triggers>
<EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DoSelectionChangedCommand}"/>
</EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
Related
I have a simple thing to code, i checked other questions but couldn't it yet.
I have an application which loads some data from an xml file retrieved from the web, and then displays it inside a longlistselector.
I did it, it works, now i would like to add an indeterminate progressbar which stays active until I finished the data loading.
I enclosed the progressbar in a stackpanel, before my longlistselector, and i bound its visibility to the function ProgressBarVisibility (see code below).
<phone:PivotItem Header="Status">
<StackPanel>
<ProgressBar Value ="0" IsIndeterminate="True" Visibility="{Binding ProgressBarVisibility}"/>
<phone:LongListSelector Margin="0,0,-12,0" ItemsSource="{Binding PivotOne}">
<phone:LongListSelector.ItemTemplate>
<!-- lots of code here -->
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</StackPanel>
</phone:PivotItem>
In the MainViewModel.cs , that's how i wrote the thing.
using System.Windows;
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.PivotOne = new ObservableCollection<ItemViewModel>();
this.PivotTwo = new ObservableCollection<ItemViewModel>();
this.PivotThree = new ObservableCollection<ItemViewModel>();
}
/// <summary>
/// A collection for ItemViewModel objects.
/// </summary>
public ObservableCollection<ItemViewModel> PivotOne { get; private set; }
public ObservableCollection<ItemViewModel> PivotTwo { get; private set; }
public ObservableCollection<ItemViewModel> PivotThree { get; private set; }
private string _detailPageTitle = "Default";
/// <summary>
/// DetailPageTitle ritorna il titolo della pagina di dettaglio. Viene settato nella funzione che carica la pagina secondaria
/// </summary>
/// <returns></returns>
public string DetailPageTitle
{
get
{
return _detailPageTitle;
}
set
{
if (value != _detailPageTitle)
{
_detailPageTitle = value;
NotifyPropertyChanged("DetailPageTitle");
}
}
}
public bool IsDataLoaded
{
get;
private set;
}
private Visibility _progressBarVisibility = Visibility.Collapsed;
public Visibility ProgressBarVisibility
{
get
{
return _progressBarVisibility;
}
set
{
if (value != _progressBarVisibility)
{
_progressBarVisibility = value;
NotifyPropertyChanged("ProgressBarVisibility");
}
}
}
private Visibility _progressBarVisibility = Visibility.Visible;
public Visibility ProgressBarVisibility
{
get
{
return _progressBarVisibility;
}
set
{
if (value != _progressBarVisibility)
{
_progressBarVisibility = value;
NotifyPropertyChanged("ProgressBarVisibility");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public void LoadData()
{
//progressbar is visible, data not loaded
this.IsDataLoaded = false;
ProgressBarVisibility = Visibility.Visible;
// Load Static and dynamic data -- populate the different pivots
LoadStaticData();
LoadXMLFile();
// data loaded, progressbar collapsed
this.IsDataLoaded = true;
ProgressBarVisibility = Visibility.Collapsed;
}
So i included system.windows library, and used the visibility class.
Anyway, i cannot get the progressbar to disappear when the loading is done, it keeps going.
Any suggestion? where am i doing it wrong?
Thanks in advance!
Solution: loaddata is executed on the app activation, so the content is not even rendered at that moment.
Your MainViewModel must implement INotifyPropertyChanged to signal to the View that one of the properties has changed. In addition, when you change the ProgressBarVisibility property, it should fire the PropertyChanged event.
There are a number of MVVM frameworks that come with some implementation of INotifyPropertyChanged, but you could easily implement something simple yourself.
You need to report the changed made to the view:
Change
public Visibility ProgressBarVisibility { get; set; }
by
private Visibility _progressBarVisibility;
public Visibility ProgressBarVisibility
{
get { return _progressBarVisibility;}
set { _progressBarVisibility = value; RaisePropertyChanged("ProgressBarVisibility");}
}
Make sure you implement INotifyPropertyChanged or a base ViewModel that implement it (MVVMLigth : ViewModelBase).
I'm creating a Windows 8 app, and I'm struggling the last couple of days with a custom user control. I can't really figure out whats wrong.
The weird thing is that the dependencyproperty calls the propertychanged event when I change Source in code, but with the binding its not updating.
So here's my code:
GamePage.xaml.cs
public sealed partial class GamePage
{
GamePageViewModel viewModel;
public GamePage()
{
this.InitializeComponent();
viewModel = new GamePageViewModel();
}
}
GamePage.xaml
<common:LayoutAwarePage x:Class="WordSearcher.GamePage"
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:common="using:WordSearcher.Common"
xmlns:controls="using:WordSearcher.Controls"
xmlns:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
d:DesignHeight="768"
d:DesignWidth="1366"
DataContext="{Binding GamePageViewModel, Source={StaticResource Locator}}">
<StackPanel Orientation="Horizontal">
<StackPanel.Background>
<ImageBrush ImageSource="Assets/Wood.jpg" Stretch="UniformToFill"/>
</StackPanel.Background>
<controls:PuzzleControl Source="{Binding Path=PuzzleData}"/>
</StackPanel>
</common:LayoutAwarePage>
GamePageViewModel.cs
public class GamePageViewModel : ViewModelBase
{
private List<string> _puzzleData;
public List<string> PuzzleData
{
get
{
return _puzzleData;
}
set
{
this._puzzleData = value;
RaisePropertyChanged("PuzzleData");
}
}
public GamePageViewModel()
{
SetNewData();
}
private async void SetNewData()
{
await SomeManager.Prepare();
PuzzleData = SomeManager.Create(20);
}
}
PuzzleControl.xaml.cs
<UserControl
x:Class="WordSearcher.Controls.PuzzleControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WordSearcher.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="500"
d:DesignWidth="500">
<Grid x:Name="puzzleGrid"
Width="500"
Height="500"
>
</Grid>
</UserControl>
PuzzleControl.xaml.cs
public sealed partial class PuzzleControl : UserControl
{
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(ObservableCollection<string>), typeof(PuzzleControl), new PropertyMetadata(null, PropertyChanged));
private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//
// not called from binding
//
((PuzzleControl)d).OnItemsSourcePropertyChanged(e);
}
private void OnItemsSourcePropertyChanged(DependencyPropertyChangedEventArgs e)
{
Source = (ObservableCollection<string>)e.NewValue;
SetGridData();
}
public ObservableCollection<string> Source
{
get
{
return (ObservableCollection<string>)GetValue(SourceProperty);
}
set
{
SetValue(SourceProperty, value);
}
}
public PuzzleControl()
{
this.InitializeComponent();
CreateRowsAndColumns();
}
private void CreateRowsAndColumns()
{
//create rows and columns in puzzleGrid
//works fine
}
private void SetGridData()
{
//fill puzzleGrid with data
//works fine
}
}
Does anyone knows with is wrong in my code? Because when I put Source = new ObservableCollection(); in the constructor of PuzzleData, the PropertyChanged event will raise. Is it anything with the DataContext?
Thnx in advance!
I don't know for sure,
but you set <controls:PuzzleControl Source="{Binding Path=PuzzleData}"/>
PuzzleData = List<string>
and
Source = ObservableCollection<string>
If the binding even works the first time (what it apperantly does) then it might be the case the source is set to List<string> in some way instead of ObservableCollection<string>. That might be the case why your dependencyproperty method (PropertyChanged) is not called because it is registered to ObservableCollection<string>.
But this is all pure speculation, haven't tested it.
After I got his code an reviewed it I found out that the PuzzleData was never really set and that that was the error... False Alarm....
Are you sure binding context? And How binding object? If you use your user control like in a gridview, DataContext is changed, and differend Datacontext of root page.
<controls:PuzzleControl Source="{Binding Path=DataContext.PuzzleData}"/>
if you use sub control, your user control; bind ElementName property like this:
<controls:PuzzleControl Source="{Binding Path=DataContext.PuzzleData, ElementName=pageRoot}"/>
If you not sure, tracing DataContext binding values on debug via breakpoints.
I am trying to bind the visibility of datagrid as GreatTall1 suggested in his post from this thread
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace XYZ.Controls
{
public class ExtendedDataGridTextColumn : DataGridTextColumn
{
private readonly Notifier _e;
private Binding _visibilityBinding;
public Binding VisibilityBinding
{
get { return _visibilityBinding; }
set
{
_visibilityBinding = value;
_e.SetBinding(Notifier.MyVisibilityProperty, _visibilityBinding);
}
}
public ExtendedDataGridTextColumn()
{
_e = new Notifier();
_e.PropertyChanged += ToggleVisibility;
}
private void ToggleVisibility(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Visibility")
this.Visibility = _e.MyVisibility;
}
//Notifier class is just used to pass the property changed event back to the column container Dependency Object, leaving it as a private inner class for now
private class Notifier : FrameworkElement, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Visibility MyVisibility
{
get { return (Visibility)GetValue(MyVisibilityProperty); }
private set { SetValue(MyVisibilityProperty, value); }
}
public static readonly DependencyProperty MyVisibilityProperty = DependencyProperty.Register("MyVisibility", typeof(Visibility), typeof(Notifier), new PropertyMetadata(MyVisibilityChanged));
private static void MyVisibilityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var n = d as Notifier;
if (n != null)
{
// n.MyVisibility = (Visibility) e.NewValue;
n.PropertyChanged(n, new PropertyChangedEventArgs("Visibility"));
}
}
}
}
I have added the class to my solution, and try to bind the DataGridTextColumn visibility to my IdVisibility property
<sdk:DataGrid x:Name="dgResult" Grid.Row="0" VerticalAlignment="Stretch" SelectionMode="Single" HorizontalAlignment="Stretch" AutoGenerateColumns="False" ItemsSource="{Binding IdSearchList}" IsReadOnly="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="10" Visibility="{Binding IsResultVisible}">
<extDg:ExtendedDataGridTextColumn Binding="{Binding Id}" VisibilityBinding="{Binding IdVisibility}" Header="Asset ID" Width="50"/>
private Visibility idVisibility = Visibility.Collapsed;
public Visibility IdVisibility
{
get { return idVisibility; }
set { idVisibility = value; NotifyPropertyChanged("IdVisibility"); }
}
The column is always being displayed regardless of the value of IdVisibility.
Please, could someone point out what i am doing wrong? This is a SL4 app
Thanks
I Think You Should Use Visiblity Converter Also You Can Bind With Designer with This Converter
Public NotInheritable Class VisibilityConverter
Implements IValueConverter
Public Function Convert(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert
If Not targetType Is GetType(Visibility) Then
Throw New ArgumentOutOfRangeException("targetType", "VisibilityConverter can only convert to Visibility")
End If
Dim _visibility As Visibility = Visibility.Visible
If value Is Nothing Then
_visibility = Visibility.Collapsed
End If
If TypeOf (value) Is Boolean Then
_visibility = IIf(value, Visibility.Visible, Visibility.Collapsed)
End If
If TypeOf (value) Is String Then
_visibility = IIf(String.IsNullOrEmpty(value), Visibility.Collapsed, Visibility.Visible)
End If
Return _visibility
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack
Throw New ArgumentOutOfRangeException("targetType", "VisibilityConverter can only convert to Boolean")
End Function
End Class
i have an combox control defined with events in my mainpage.xaml
<Grid x:Name="LayoutRoot">
<ComboBox SelectionChanged="ComboBox_SelectionChanged"></ComboBox>
</Grid>
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
now how do we defined events for combox control in mvvm model .
and how do we bind the collection list to combo box. i am using SL 3
thanks
prince
In your xaml, you can bind the ItemSource and SelectedItem as shown below:
MainPage.xaml
<UserControl x:Class="App1.MainPage"
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:App1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.DataContext>
<local:MainPage_ViewModel/>
</UserControl.DataContext>
<Grid x:Name="LayoutRoot" Background="White">
<ComboBox ItemsSource="{Binding MyItems}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" SelectionChanged="ComboBox_SelectionChanged" Height="30" Width="100"/>
</Grid>
In the MainPage.xaml.cs, your Selection changed method could just call the method on your ViewModel since you are using SL3:
MainPage.xaml.cs
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private MainPage_ViewModel viewModel
{
get { return this.DataContext as MainPage_ViewModel; }
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
this.viewModel.SelectionChanged();
}
}
Your ViewModel would have the MyItems collection and the SelectedItem to bind to:
MainPage_ViewModel.cs
public class MainPage_ViewModel : INotifyPropertyChanged
{
public ObservableCollection<string> MyItems
{
get { return myItems; }
set { myItems = value; }
}
private ObservableCollection<string> myItems = new ObservableCollection<string>() { "One", "Two", "Three" };
public string SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; }
}
private string selectedItem = string.Empty;
public void SelectionChanged()
{
//Perform logic that needs to happen when selection changes
}
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Depending on what you were using your SelectionChanged method for, you may no longer need it since this would bind the SelectedItem to the ViewModel.
Hope this helps!
I have a data class that implements INotifyPropertyChanged and two WPF controls that DataBind to the same value. Only one WPF control is updated, why?
Here is my data class:
using System;
using System.ComponentModel;
using System.Windows.Threading;
namespace TestMultiBind
{
class DataSource : INotifyPropertyChanged
{
static int _DataValue;
static DispatcherTimer tmr = new DispatcherTimer();
static int _ClientCount = 0;
#region InotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public int DataValue
{
get { return _DataValue; }
}
public DataSource()
{
if (!DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject()))
{
_ClientCount = _ClientCount + 1;
if (!tmr.IsEnabled)
{
tmr.Interval = TimeSpan.FromMilliseconds(10);
tmr.Tick += new EventHandler(tmr_Tick);
tmr.Start();
}
}
}
void tmr_Tick(object sender, EventArgs e)
{
_DataValue = DateTime.Now.Second;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("DataValue"));
}
}
}
}
and here is my XAML
Title="Window1" Height="300" Width="300">
<Grid>
<StackPanel>
<ProgressBar Height="20" Name="progressBar1" Value="{Binding Mode=OneWay, Path=DataValue}" Maximum="60">
<ProgressBar.DataContext>
<ds:DataSource/>
</ProgressBar.DataContext>
</ProgressBar>
<ProgressBar Height="30" Name="progressBar2" Value="{Binding Mode=OneWay, Path=DataValue}" Maximum="60">
<ProgressBar.DataContext>
<ds:DataSource/>
</ProgressBar.DataContext>
</ProgressBar>
</StackPanel>
</Grid>
I have tried to present the simplest example possible, thus I created a data class that uses a timer to update a value once per second. The real world problem I am trying to solve is data coming across a serial port that is to be displayed. Thus I need to store static data for the incoming data within the class as well as handling instance specific events for each of the clients (WPF controls) that are data bound to the control.
It would appear that my tmr_Tick routine is somehow instance specific to the first client as opposed to being static to the class and raising the multiple events required for each of the clients.
What am I missing here or doing wrong?
I'm not sure if this is the best way to do this, but it works. The problem with my initial code was that I needed both a static and a instance constructor for the class. The timer is created in the static constructor (which is only run once) while there needs to be one event handler per binding so this is handled in the instance constructor.
Here is the code that works:
using System;
using System.ComponentModel;
using System.Windows.Threading;
namespace TestMultiBind
{
class DataSource : INotifyPropertyChanged
{
static int _DataValue;
static DispatcherTimer tmr = new DispatcherTimer();
#region InotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public int DataValue
{
get { return _DataValue; }
}
static DataSource()
{
if (!DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject()))
{
tmr.Interval = TimeSpan.FromMilliseconds(10);
tmr.Start();
}
}
public DataSource()
{
if (!DesignerProperties.GetIsInDesignMode(new System.Windows.DependencyObject()))
{
tmr.Tick += new EventHandler(tmr_Tick);
}
}
void tmr_Tick(object sender, EventArgs e)
{
_DataValue = DateTime.Now.Second;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("DataValue"));
}
}
}
}