i would like to ask how to Populate listview using MVVM pattern i'm beginner in mvvm pattern, i learn more from doing things than reading. i've done this before using wpf but i use code behind.
I use Mvvm Light. What i want is browse the Location of the folder and then populate the listview with the files inside it
so far i already have a Browse folder
i have this code
public class OpenFileDialogVM : ViewModelBase
{
public static RelayCommand OpenCommand { get; set; }
private string _selectedPath;
public string SelectedPath
{
get { return _selectedPath; }
set
{
_selectedPath = value;
RaisePropertyChanged("SelectedPath");
}
}
private string _defaultPath;
public OpenFileDialogVM()
{
RegisterCommands();
}
public OpenFileDialogVM(string defaultPath)
{
_defaultPath = defaultPath;
RegisterCommands();
}
private void RegisterCommands()
{
OpenCommand = new RelayCommand(ExecuteOpenFileDialog);
}
private void ExecuteOpenFileDialog()
{
var dialog = new FolderBrowserDialog();
dialog.ShowDialog();
SelectedPath = dialog.SelectedPath;
}
}
and i have this code for user control
<UserControl x:Class="MvvmLight1.FolderDialog"
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:vm="clr-namespace:MvvmLight1"
xmlns:local="clr-namespace:MvvmLight1"
mc:Ignorable="d" d:DesignWidth="300" Height="186.916" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="90*"/>
<RowDefinition Height="97*"/>
</Grid.RowDefinitions>
<Grid>
<TextBox Text="{Binding SelectedPath}" />
</Grid>
<Grid Grid.Row="1" >
<Button Command="vm:OpenFileDialogVM.OpenCommand" >Browse</Button>
</Grid>
</Grid>
</UserControl>
so far the browse is working. my question is how can i call this code. after the selection of folder so that i can populate my listview?
private void Call(string selectedpath)
{
try
{
var allFiles = Directory.GetFiles(selectedpath, "*", SearchOption.AllDirectories);
foreach (var item in allFiles)
{
System.Console.WriteLine(item);
//code for populating listview
}
}
catch (System.Exception ex)
{
System.Console.WriteLine(ex.StackTrace);
throw ex;
}
}
Thank you for time.
Make Files a public observable collection.
Raise the PropertyChanged event.
Set the window's datacontext to your viewmodel.
Bind to the viewmodel's property in xaml.
CS
using System;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Input;
using System.Collections.ObjectModel;
namespace StackOverflow_PopulateListView
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new OpenFileDialogVM();
}
}
public class OpenFileDialogVM : ViewModelBase
{
public static RelayCommand OpenCommand { get; set; }
private string _selectedPath = "Enter a Path";
public string SelectedPath
{
get { return _selectedPath; }
set
{
_selectedPath = value;
OnPropertyChanged("SelectedPath");
}
}
private ObservableCollection<string> _files = new ObservableCollection<string>() { "Tits", "balls", "ass", "tits" };
public ObservableCollection<string> Files
{
get { return _files; }
set
{
_files = value;
OnPropertyChanged("Files");
}
}
private ICommand _selectFileCommand;
public ICommand SelectFileCommand
{
get
{
return _selectFileCommand ?? (_selectFileCommand = new RelayCommand(() => Call(SelectedPath)));
}
protected set
{
_selectFileCommand = value;
}
}
public void Call(string selectedpath)
{
try
{
Files = new ObservableCollection<string>(Directory.GetFiles(selectedpath, "*", SearchOption.AllDirectories));
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
throw ex;
}
}
}
public class RelayCommand : ICommand
{
public Action Act { get; set; }
/// <summary> Occurs when the target of the Command should reevaluate whether or not the Command can be executed. </summary>
public event EventHandler CanExecuteChanged;
public RelayCommand(Action act)
{
Act = act;
}
/// <summary> Returns a bool indicating if the Command can be exectued with the given parameter </summary>
public bool CanExecute(object obj)
{
return true;
}
/// <summary> Send a ICommand.CanExecuteChanged </summary>
public void ChangeCanExecute()
{
object sender = this;
EventArgs eventArgs = null;
CanExecuteChanged(sender, eventArgs);
}
/// <summary> Invokes the execute Action </summary>
public void Execute(object obj)
{
Act();
}
}
/// <summary>
/// Extremely generic ViewModelBase; for copy-pasting into almost any MVVM project
/// </summary>
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Fires PropertyChangedEventHandler, for bindables
/// </summary>
protected virtual void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
XAML
<Window x:Class="StackOverflow_PopulateListView.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:local="clr-namespace:StackOverflow_PopulateListView"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="8*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding Files}" Grid.Row="0">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBox Grid.Row="1"
Text="{Binding SelectedPath}"
/>
<Button Grid.Row="2"
Content="Call"
Command="{Binding SelectFileCommand}"
/>
</Grid>
</Window>
Your view model should have an ObservableCollection of file names
public ObservableCollection<string> FileNames { get; }
= new ObservableCollection<string>();
which is populated when a directory is selected:
var files = Directory.EnumerateFiles(selectedpath, "*", SearchOption.AllDirectories);
FileNames.Clear();
foreach (var file in files)
{
FileNames.Add(file);
}
You would then bind the ItemsSource property of a ListBox to that collection:
<ListBox ItemsSource="{Binding FileNames}"/>
Related
I'm a newbie in wpf and i know that this question has been asked other times, and i tried to implement some solutions that i found. But it's not working. I'm doing something wrong but i can't see what it is.
I've created a new simple application to test this problem.
namespace WpfApp3
{
public class MyElement
{
public string Text { get; set; }
public MyElement(string t)
{
Text = t;
}
}
public class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_handler(parameter);
}
private Action<object> _handler;
public MyCommand(Action<object> handler) { _handler = handler; }
}
public class MyItemsControlViewModel
{
ObservableCollection<MyElement> _items;
public ObservableCollection<MyElement> MyElementItems { get { return _items; } set { _items = value; RaisePropertyChanged("MyElementItems"); } }
ObservableCollection<MyElement> _temporayList;
private ICommand _itemClicked;
public ICommand ItemClicked { get { return _itemClicked; } }
public MyItemsControlViewModel()
{
_items = new ObservableCollection<MyElement>();
_temporayList = new ObservableCollection<MyElement>();
_itemClicked = new MyCommand(OnItemSelected);
AddItem("Element 1");
AddItem("Element 2");
AddItem("Element 3");
UpdateList();
}
public void UpdateList()
{
MyElementItems = _temporayList;
}
public void AddItem(string t)
{
MyElement item = new MyElement(t);
_temporayList.Add(item);
}
public void OnItemSelected(object param)
{
Debug.WriteLine("Executed!");
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML
<UserControl x:Class="WpfApp3.MyUserControl"
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:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="1080" d:DesignWidth="570"
x:Name="myCustomControl">
<Grid >
<Button x:Name="btnOutsideItemsControl" Width="100" Height="100 " VerticalAlignment="Top" Command="{Binding ItemClicked}" />
<ItemsControl
x:Name="listItems"
ScrollViewer.PanningMode="None"
IsEnabled="False"
Background = "Transparent"
HorizontalAlignment="Center"
ItemsSource="{Binding MyElementItems}" Margin="0,152,0,0" Width="549">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" Margin="50,0,0,0"
Background="Transparent" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Content="{Binding Text}"
Command="{Binding ElementName=listItems, Path=DataContext.ItemClicked}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
The component is used in MainWindow.xaml.
namespace WpfApp3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MyItemsControlViewModel _myViewModel;
public MyItemsControlViewModel MyViewModel { get { return _myViewModel; } }
public MainWindow()
{
_myViewModel = new MyItemsControlViewModel();
InitializeComponent();
myCustomControl.DataContext = MyViewModel;
}
}
}
XAML
<Window x:Class="WpfApp3.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:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:MyUserControl x:Name="myCustomControl"/>
</Grid>
</Window>
When i run the application i can see correctly the list of 3 items with the correct text.
But if i click on one of the button of the list i can't see the output of Debug.WriteLine("Executed!");
But if i click on the button btnOutsideItemsControl that is outside the ItemsControl, it works. I can see the output of Debug.WriteLine("Executed!");
So i think that also the definition of the command is correct.
To bind correctly the Command property of Button inside the ItemsControl i try this
<Button Command="{Binding ElementName=listItems, Path=DataContext.ItemClicked}">
And also this
<Button Command="{Binding DataContext.ItemClicked, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}">
But it not works.
Please help!
You're gonna kick yourself once I tell you.
Your problem is that you set IsEnabled="False" on your ItemsControl. Remove it and all will be well with the universe.
I just started learning MVVM and I want to know what the best practice would be when I want to change content from my MainWindow when I click a button from inside my UserControl.
This is the UserControl which contains the buttons that I will be clicking. When I click on the IconButtons this produces, I want to be able to change the MainGrid to a Grid containing the information from my IconModel.
For clarification, I would like the Grid to show the Icon's Name, Description, Username, Password, and have a button that would open up either a program or a website using Path. The Icon class is inside IconModel.cs which is posted below.
IconView.xaml
<UserControl x:Class="ProgramManager.Views.IconView"
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:ProgramManager"
xmlns:viewModel="clr-namespace:ProgramManager.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<viewModel:IconViewModel/>
</UserControl.DataContext>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Path = Icons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Name="IconButton" Style="{StaticResource IconButtonStyle}" Content="{Binding Path = Initial, Mode = OneWay}" Click="Button_Click">
<Button.ToolTip>
<ToolTip Content="{Binding Path = Name, Mode = TwoWay}"/>
</Button.ToolTip>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</UserControl>
MainWindow.xaml
<Window x:Class="ProgramManager.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:local="clr-namespace:ProgramManager"
xmlns:views="clr-namespace:ProgramManager.Views"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid Name="MainGrid" Grid.Column="1">
</Grid>
</Grid>
</Window>
For reference, here are my IconModel and IconViewModel classes.
IconModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Xml.Serialization;
namespace ProgramManager.Model
{
public class IconModel { }
[XmlRoot(ElementName = "Icons", IsNullable = true, DataType = "string")]
[XmlType(AnonymousType = true)]
[XmlInclude(typeof(ProgramIcon))]
[XmlInclude(typeof(WebsiteIcon))]
public class Icon : IComparable<Icon>, IComparer<Icon>, INotifyPropertyChanged
{
private string name;
private string description;
private string path;
private string username;
private string password;
[XmlElement("Name")]
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
RaisePropertyChanged("Name");
}
}
}
[XmlElement("Description")]
public string Description
{
get { return description; }
set
{
if (description != value)
{
description = value;
RaisePropertyChanged("Description");
}
}
}
[XmlElement("Path")]
public string Path
{
get { return path; }
set
{
if (path != value)
{
path = value;
RaisePropertyChanged("Path");
}
}
}
[XmlElement("Username")]
public string Username
{
get { return username; }
set
{
if (username != value)
{
username = value;
RaisePropertyChanged("Username");
}
}
}
[XmlElement("Password")]
public string Password
{
get { return password; }
set
{
if (password != value)
{
password = value;
RaisePropertyChanged("Password");
}
}
}
public string Initial {
get
{
return this.Name.Substring(0, 1).ToUpper();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public int Compare(Icon x, Icon y)
{
return (x.Equals(null) || y.Equals(null)) ? 0 : x.CompareTo(y);
}
public int CompareTo(Icon other)
{
return string.Compare(this.Name, other.Name);
}
}
public class ProgramIcon : Icon { }
public class WebsiteIcon : Icon { }
}
IconViewModel.cs
using ProgramManager.Helpers;
using ProgramManager.Model;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
namespace ProgramManager.ViewModel
{
public class IconViewModel : INotifyPropertyChanged
{
private static readonly List<Icon> iconsList;
private ObservableCollection<Icon> icons;
public ObservableCollection<Icon> Icons
{
get { return icons; }
set
{
if (icons != value)
{
icons = value;
RaisePropertyChanged("Icons");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public IconViewModel()
{
LoadIcons();
}
static IconViewModel()
{
if (!Directory.Exists(Constants.ResourcesDirectory))
{
Directory.CreateDirectory(Constants.ResourcesDirectory);
}
iconsList = XmlDataStorage.GetList<Icon>(Constants.IconsFilePath);
}
public void LoadIcons()
{
ObservableCollection<Icon> icons = new ObservableCollection<Icon>();
foreach(var icon in iconsList)
{
icons.Add(icon);
}
Icons = icons;
}
public void AddIcon(Icon icon)
{
iconsList.Add(icon);
iconsList.Sort();
XmlDataStorage.SaveToXml(Constants.IconsFilePath, iconsList);
LoadIcons();
}
}
}
I have tried accessing the IconButton in MainWindow from the IconView.xaml, but I can't seem to figure out how to do it.
Any help is appreciated, thank you!
You can access the main window like Application.Current.MainWindow.
For MVVM solution you can do somethig like (Take the idea, I didn't compiled this):
inside MainWindow.xaml:
<Label DataContext="{IconViewModel }" Content={Binding YourProperty}/>
I mean you can set the data context of the label to be the IconViewModel and the to bind the property from IconViewModel class.
third option is to create an event inside IconViewModel and when you click the button to invoke the event, and the mainwindow will subscribe to this event.
Why don't you change the IconView.xaml to a ContentTemplate?And the IconButton Binding a dependency property.So that you can operate the IconButton anywhere.Iclude the MainWindow.
How to write WPF OpenFileDialog using MVVM (Model-View-ViewModel) in c#?
I have visited some websites regarding this OpenFileDialog ,but I didn't get clear idea about it. This is my code
In View.xaml:
<Window.... xmlns:VM="clr-namespace:myproject.myViewModel"
... >
<Window.DataContext><VM:myViewModel/>
</Window.DataContext>
<ItemsControl ItemsSource="{Binding mygroup}" >
<ItemsControl.ItemTemplate >
<DataTemplate>
<Grid >....
<TextBlock Text="Color" Margin="20" VerticalAlignment="Center"/>
<ComboBox KeyboardNavigation.TabIndex="0" Grid.Column="1" Margin="45,10,10,10" Height="30" Width="200" ItemsSource="{Binding Color}" />
<TextBlock Text="Shapes" Grid.Row="1" VerticalAlignment="Center" />
<ComboBox KeyboardNavigation.TabIndex="3" Grid.Column="1" Grid.Row="1" Height="20" Width="150" SelectedIndex="0" HorizontalContentAlignment="Right"
VerticalAlignment="Center" ItemsSource="{Binding Shapes}">
</ComboBox>
<TextBlock Text="Open Files " VerticalAlignment="Center" Grid.Row="0" Grid.Column="2" Margin="10" />
<TextBox Grid.Column="3" Text="" Height="30" Grid.Row="0" IsReadOnly="True"
TextAlignment="Right" VerticalContentAlignment="Center" Width="200" /> <Button Grid.Column="4" Content="Browse" Height="30" VerticalAlignment="Bottom" MinWidth="41" />
</Grid>
</Window>
In Model.cs:
namespace Myproject.Models
{
public class ProjectModel : INotifyPropertyChanged
{
private ObservableCollection<string> color;
private ObservableCollection<string> shapes;
public ObservableCollection<string> Color
{
get { return color; }
set
{
color = value;
NotifyPropertyChanged("Color");
}
}
public ObservableCollection<string> Shapes
{
get { return shapes; }
set
{
shapes = value;
NotifyPropertyChanged("Shapes");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Private Helpers
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
In ViewModel.cs:
namespace MyProject.ViewModels
{
public class myProjectViewModel : INotifyPropertyChanged
{
private ObservableCollection<myprojectmodel> mygroup;
public ObservableCollection<myprojectmodel> Mygroup
{
get => this.mygroup;
set
{
if (Equals(value, this.mygroup)) return;
this.mygroup = value;
OnPropertyChanged();
}
}
public ProjectViewModel()
{
Mygroup = new ObservableCollection<myprojectmodel>();
List<string> lstColor = new List<string>();
lstCity = new List<string> {"Pink", "Blue", "Red"};
List<string> lstShapes = new List<string>();
lstTemperature = new List<string> {"Rectangle", "Triangle", "Circle"};
Mygroup.Add(
new ProjectModel
{
Color= new ObservableCollection<string>(lstColor),
Shapes = new ObservableCollection<string>(lstShapes),
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
How should I write code to get OpenFileDialog.
I have seen this link WPF OpenFileDialog with the MVVM pattern? but I don't know how to write it for my above code.
please someone help me by editing my code to get OpenFileDailog.
In my opinion this kind of things doesn't belong in to the ViewModel. It's View specific logic. The View alone handles user input and then sends it to the ViewModel. ViewModel never asks the View to do something. This would invert the dependency chain and couple the ViewModel to the View. The dependencies have to be like this:
View --> ViewModel --> Model. The ViewModel doesn't even know about the type of views nor that there is a View at all.
You have to open the dialog from your view and then send the result to the view model.
For this you could create a simple event handler in your code-behind and attach it to a button's click event. You take the picked file and use an ICommand to invoke e.g. an open file action. That's MVVM (or MVP). Separate the concerns of the views from your models.
MainWindow.xaml:
<Window x:Class="WpfOpenDialogExample.OpenFileDialogSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="OpenFileDialogSample" Height="300" Width="300">
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<Grid>
<Button Name="ShowFilePickerButton" Click="ShowFilePicker_OnClick" Content="Open file" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System;
using System.IO;
using System.Windows;
using Microsoft.Win32;
namespace WpfOpenDialogExample
{
public partial class OpenFileDialogSample : Window
{
public OpenFileDialogSample()
{
InitializeComponent();
}
private void ShowFilePicker_OnClick(object sender, RoutedEventArgs e)
{
var viewModel = this.DataContext as ViewModel;
OpenFileDialog openFileDialog = new OpenFileDialog();
if(openFileDialog.ShowDialog() == true && viewModel.OpenFileCommand.CanExecute(openFileDialog.FileName))
{
viewModel.OpenFileCommand.Execute(openFileDialog.FileName);
}
}
private void ShowFolderPicker_OnClick(object sender, RoutedEventArgs e)
{
var viewModel = this.DataContext as ViewModel;
FolderBrowserDialog openFolderDialog = new FolderBrowserDialog();
if(openFolderDialog.ShowDialog() == DialogResul.Ok && viewModel.OpenFolderCommand.CanExecute(openFolderDialog.SelectedPath ))
{
viewModel.OpenFolderCommand.Execute(openFolderDialog.SelectedPath );
}
}
}
}
ViewModel.cs:
public ICommand OpenFileCommand { get => new RelayCommand(OpenFile, CanOpenFile); }
private void OpenFile(string filePath)
{
...
}
private bool CanOpenFile(string filePath)
{
return File.Exists(filePath);
}
public ICommand OpenFolderCommand { get => new RelayCommand(OpenFolder, CanOpenFolder); }
private void OpenFolder(string folderPath)
{
...
}
private bool CanOpenFolder(string folderPath)
{
return Directory.Exists(filePath);
}
RelayCommand.cs:
using System;
using System.Windows.Input;
namespace WpfOpenDialogExample
{
/// <summary>
/// An implementation independent ICommand implementation.
/// Enables instant creation of an ICommand without implementing the ICommand interface for each command.
/// The individual Execute() an CanExecute() members are suplied via delegates.
/// <seealso cref="System.Windows.Input.ICommand"/>
/// </summary>
/// <remarks>The type of <c>RelaisCommand</c> actually is a <see cref="System.Windows.Input.ICommand"/></remarks>
public class RelayCommand : ICommand
{
/// <summary>
/// Default constructor to declare the concrete implementation of Execute(object):void and CanExecute(object) : bool
/// </summary>
/// <param name="executeDelegate">Delegate referencing the execution context method.
/// Delegate signature: delegate(object):void</param>
/// <param name="canExecuteDelegate">Delegate referencing the canExecute context method.
/// Delegate signature: delegate(object):bool</param>
public RelayCommand(Action<object> executeDelegate , Predicate<object> canExecuteDelegate)
{
this.executeDelegate = executeDelegate;
this.canExecuteDelegate = canExecuteDelegate;
}
/// <summary>
/// Invokes the custom <c>canExecuteDelegate</c> which should check wether the command can be executed.
/// </summary>
/// <param name="parameter">Optional parameter of type <see cref="System.Object"/></param>
/// <returns>Expected to return tue, when the preconditions meet the requirements and therefore the command stored in <c>executeDelegate</c> can execute.
/// Expected to return fals when command execution is not possible.</returns>
public bool CanExecute(object parameter)
{
if (this.canExecuteDelegate != null)
{
return this.canExecuteDelegate(parameter);
}
return false;
}
/// <summary>
/// Invokes the custom <c>executeDelegate</c>, which references the command to execute.
/// </summary>
/// <param name="parameter">Optional parameter of type <see cref="System.Object"/></param>
public void Execute(object parameter)
{
if (this.executeDelegate != null)
this.executeDelegate(parameter);
}
/// <summary>
/// The event is triggered every time the conditions regarding the command have changed. This occures when <c>InvalidateRequerySuggested()</c> gets explicitly or implicitly called.
/// Triggering this event usually results in an invokation of <c>CanExecute(object):bool</c> to check if the occured change has made command execution possible.
/// The <see cref="System.Windows.Input.CommandManager"/> holds a weakrefernce to the observer.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private readonly Action<object> executeDelegate;
private readonly Predicate<object> canExecuteDelegate;
}
}
In the process of trying to learn WFP, I have taken the task of porting some old Winform apps into WPF and trying to stick to a MVVM model.
In the Winform app, I have a set of checkboxes that change the state of a BitArray, which in turns gets sent over TCP. Simple Stuff.
How would I do this in WPF and databinding? How can i bind a specific checkbox to a specific bit in the BitArray? All the examples i have found of this databind to single boolean property in VM.
EDIT:
I found the solution here by using an ObservableCollection>:
How to bind an ObservableCollection<bool> to a Listbox of Checkboxes in WPF
What i don't understand is what is the purpose of:
public static implicit operator Wrapper<T>(T value)
{
return new Wrapper<T> { value = value };
}
public static implicit operator T(Wrapper<T> wrapper)
{
return wrapper.value;
}
Inside the wrapper class, can someone explain what this does and why its needed?
The benefit of using MVVM is that you can taylor view models to suite your needs.
Create an Item class to track the status of each bit in the array.
Create a MVVM view model with an observable collection of your Item object
Databind your view model in code behind
Decorate your xaml with binding info
That's it! Enjoy!
See an Screenshot
Download a Full Example on GitHub
C#
using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
namespace DataBindingBitArray
{
/// <summary>
/// 1. Create an Item class to track the status of each bit in the array.
/// </summary>
/// <seealso cref="System.ComponentModel.INotifyPropertyChanged" />
public class Item : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int BitArrayIndex { get; set; }
public BitArray ParentBitArray { get; set; }
private bool isChecked;
public Item(int bitArrayIndex, bool isChecked, BitArray parentBitArray)
{
this.BitArrayIndex = bitArrayIndex;
this.isChecked = isChecked;
this.ParentBitArray = parentBitArray;
}
public bool IsChecked
{
get => isChecked;
set
{
if (ParentBitArray != null)
{
ParentBitArray[BitArrayIndex] = isChecked = value;
OnPropertyChanged(nameof(IsChecked));
}
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
/// <summary>
/// 2. Create a MVVM view model with an observable collection of your Item object
/// </summary>
/// <seealso cref="System.ComponentModel.INotifyPropertyChanged" />
public class BitArrayViewModel : INotifyPropertyChanged
{
private readonly BitArray bitArray;
private ObservableCollection<Item> items;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Item> Items
{
get => items;
set
{
items = value;
OnPropertyChanged(nameof(Items));
}
}
public BitArrayViewModel(BitArray bitArray)
{
this.bitArray = bitArray;
var query = this
.bitArray
.Cast<bool>()
.Select((s, i) => new Item(i, s, this.bitArray));
this.Items = new ObservableCollection<Item>(query);
}
public int CountOnBits()
{
return this.bitArray.Cast<bool>().Count(s => s);
}
public int CountOffBits()
{
return this.bitArray.Cast<bool>().Count(s => !s);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
/// <summary>
/// 3 . Databind your view model in code behind
/// </summary>
/// <seealso cref="System.Windows.Window" />
/// <seealso cref="System.Windows.Markup.IComponentConnector" />
public partial class MainWindow : Window
{
public BitArrayViewModel ViewModel;
public MainWindow()
{
InitializeComponent();
this.DataContext = ViewModel = new BitArrayViewModel(new BitArray(100));
MessageBox.Show($"You have {ViewModel.CountOnBits()} on bits and {ViewModel.CountOffBits()} off bits");
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show($"You have {ViewModel.CountOnBits()} on bits and {ViewModel.CountOffBits()} off bits");
}
}
}
XAML
<Window x:Class="DataBindingBitArray.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:local="clr-namespace:DataBindingBitArray"
mc:Ignorable="d"
Height="360" Width="250">
<StackPanel Height="300" Margin="10">
<Label Height="40" Margin="5" FontSize="18">Binding to Bit Array</Label>
<ScrollViewer Height="200">
<ItemsControl Margin="5" x:Name="ItemsControl1" ItemsSource="{Binding Path=Items}" HorizontalAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" Content ="{Binding Path=BitArrayIndex }"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Button Height="40" Margin="5" Click="ButtonBase_OnClick" Content="Show BitArray Status"></Button>
</StackPanel>
</Window>
I would like to navigate to another page from the specific list but at the moment no matter what list i click, it will still go to the same page. What should I do. I am still new here and to learn more. Thanks.
Below are the codes.
MainPage.xaml
<!--Pivot Control-->
<phone:Pivot Title="DAILY ROUTINE">
<!--Pivot item one-->
<phone:PivotItem Header="activity">
<!--Double line list with text wrapping-->
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="LongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</phone:PivotItem>
<!--Pivot item two-->
<phone:PivotItem Header="today">
</phone:PivotItem>
</phone:Pivot>
MainPage.xaml.cs
namespace PivotApp3
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// Set the data context of the listbox control to the sample data
DataContext = App.ViewModel;
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
// Load data for the ViewModel Items
protected override void OnNavigatedTo(NavigationEventArgs e)
{
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
}
private void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
NavigationService.Navigate(new Uri("/todolistPage.xaml", UriKind.Relative));
}
}
}
todolistPage.xaml
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="TO DO LIST" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="add" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
</Grid>
</Grid>
MainViewModel.cs
namespace PivotApp3.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.Items = new ObservableCollection<ItemViewModel>();
}
/// <summary>
/// A collection for ItemViewModel objects.
/// </summary>
public ObservableCollection<ItemViewModel> Items { get; private set; }
private string _sampleProperty = "Sample Runtime Property Value";
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its value using a Binding
/// </summary>
/// <returns></returns>
public string SampleProperty
{
get
{
return _sampleProperty;
}
set
{
if (value != _sampleProperty)
{
_sampleProperty = value;
NotifyPropertyChanged("SampleProperty");
}
}
}
/// <summary>
/// Sample property that returns a localized string
/// </summary>
public string LocalizedSampleProperty
{
get
{
return AppResources.SampleProperty;
}
}
public bool IsDataLoaded
{
get;
private set;
}
/// <summary>
/// Creates and adds a few ItemViewModel objects into the Items collection.
/// </summary>
public void LoadData()
{
// Sample data; replace with real data
this.Items.Add(new ItemViewModel() { LineOne = "+ To Do List" });
this.Items.Add(new ItemViewModel() { LineOne = "+ Reminder" });
this.Items.Add(new ItemViewModel() { LineOne = "+ Expenses" });
this.IsDataLoaded = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
ItemViewModel.cs
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace PivotApp3.ViewModels
{
public class ItemViewModel : INotifyPropertyChanged
{
private string _lineOne;
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its value using a Binding.
/// </summary>
/// <returns></returns>
public string LineOne
{
get
{
return _lineOne;
}
set
{
if (value != _lineOne)
{
_lineOne = value;
NotifyPropertyChanged("LineOne");
}
}
}
private string _lineTwo;
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its value using a Binding.
/// </summary>
/// <returns></returns>
public string LineTwo
{
get
{
return _lineTwo;
}
set
{
if (value != _lineTwo)
{
_lineTwo = value;
NotifyPropertyChanged("LineTwo");
}
}
}
private string _lineThree;
/// <summary>
/// Sample ViewModel property; this property is used in the view to display its value using a Binding.
/// </summary>
/// <returns></returns>
public string LineThree
{
get
{
return _lineThree;
}
set
{
if (value != _lineThree)
{
_lineThree = value;
NotifyPropertyChanged("LineThree");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
The most trivial solution is:
private void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var si = MainLongListSelector.SelectedItem as PivotApp3.ViewModels.ItemViewModel;
if(si.LineOne.Equals("+ To Do List"))
NavigationService.Navigate(new Uri("/todolistPage.xaml", UriKind.Relative));
else if(si.LineOne.Equals("another"))
NavigationService.Navigate(new Uri("/another.xaml", UriKind.Relative));
}
You don't have to use ItemViewModel to bind some data to the list. If you will use your own class - than you can place URI "behind" your item and use it. (It will be better solution).
Example:
public void LoadData()
{
// Sample data; replace with real data
this.Items.Add(new ItemViewModel() { LineOne = "+ To Do List", GotoUri = new Uri("/todolistPage.xaml", UriKind.Relative) });
this.Items.Add(new ItemViewModel() { LineOne = "+ Reminder", GotoUri = new Uri("/other.xaml", UriKind.Relative) });
this.Items.Add(new ItemViewModel() { LineOne = "+ Expenses", GotoUri = new Uri("/other2.xaml", UriKind.Relative) });
this.IsDataLoaded = true;
}
Then:
public class ItemViewModel : INotifyPropertyChanged
{
public Uri GotoUri {get; set;}
//...
}
private void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var si = MainLongListSelector.SelectedItem as PivotApp3.ViewModels.ItemViewModel;
if(si != null)
NavigationService.Navigate(si.GotoUri, UriKind.Relative));
}