I'm currently in the process of writing a launcher and I'm in the prototyping phase of it.
I'm really new to WPF but not to the MVVM architecture, however, I'm not sure how to bind my ViewModel to the CommandBox view because I'm using methods in my ViewModel, maybe I'm looking at it from the wrong angle so if I'm doing something wrong please enlighten me! :)
I have the following ViewModel that basically is really the essence of the launcher in terms of its input:
/// <summary>
/// Provides a buffer of characters in order to search for candidates that matches the buffer.
/// </summary>
public class CommandBufferViewModel : INotifyPropertyChanged
{
private readonly StringBuilder _input;
public CommandBufferViewModel()
{
_input = new StringBuilder();
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Deletes a character from the buffer.
/// </summary>
public void DeleteKey(int index, int length = 1)
{
_input.Remove(index, length);
OnPropertyChanged(nameof(DeleteKey));
}
/// <summary>
/// Adds a character to the buffer.
/// </summary>
public void ProcessKey(int index, char key)
{
_input.Insert(index, key);
OnPropertyChanged(nameof(ProcessKey));
}
/// <summary>
/// Returns results that matches the current buffer.
/// </summary>
/// <returns>Results that matches the current buffer.</returns>
public async Task<CommandResults> Search()
{
// NYI
return default(CommandResults);
}
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The idea here is that commands can be stored in a remote database or whatever storage you may like as well as locally.
The (CommandBox view) TextBox looks like this:
<TextBox
x:Class="Yalla.Launcher.Views.CommandBox"
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:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:bs="clr-namespace:Yalla.Launcher.Behaviors"
mc:Ignorable="d"
Width="Auto" Height="Auto" VerticalContentAlignment="Center" BorderThickness="1">
<i:Interaction.Behaviors>
<bs:WatermarkBehavior Text="Enter Command... (type ? for help)" />
</i:Interaction.Behaviors>
</TextBox>
I think I figured this out, I'll simply have a ViewModel that holds the text that is the command the user is searching for and I'll use this as the glue between the view and the search service of the launcher.
Something like this:
namespace Yalla.Launcher.ViewModels
{
using System.ComponentModel;
using Commands;
using Features.Commands.Search;
public class SearchViewModel : INotifyPropertyChanged, ISearchableText
{
private SearchCommandHandler _enterCommandHandler;
private string _searchText;
public event PropertyChangedEventHandler PropertyChanged;
public SearchCommandHandler Search => _enterCommandHandler ?? (_enterCommandHandler = new SearchCommandHandler(new SearchService(this)));
public string SearchText
{
get
{
return _searchText;
}
set
{
_searchText = value;
OnPropertyChanged(nameof(SearchText));
}
}
protected virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Finally, I bind it to the TextBox like this:
<ctrls:LauncherTextBox
x:Class="Yalla.Launcher.Views.LauncherSearchBox"
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:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:bs="clr-namespace:Yalla.Launcher.Behaviors"
xmlns:ctrls="clr-namespace:Yalla.Launcher.Controls"
xmlns:vm="clr-namespace:Yalla.Launcher.ViewModels"
mc:Ignorable="d"
Width="Auto" Height="Auto"
VerticalContentAlignment="Center" BorderThickness="1"
Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ctrls:LauncherTextBox.DataContext>
<vm:SearchViewModel />
</ctrls:LauncherTextBox.DataContext>
<i:Interaction.Behaviors>
<bs:WatermarkBehavior Text="Enter Command... (type ? for help)" />
</i:Interaction.Behaviors>
<TextBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding Search}"></KeyBinding>
</TextBox.InputBindings>
</ctrls:LauncherTextBox>
Quite simple but that's probably what I need to start with.
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 have searched a lot but cannot get what I want. I need to fill a combo box with images (114 images embedded in Resources.resx).
I am just getting list, not images. Here is my code.
ResourceSet rsrcSet =MyProject.Properties.Resources.ResourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true);
List<object> images = new List<object>();
foreach (DictionaryEntry entry in rsrcSet)
{
//String name = entry.Key.ToString();
//Object resource = entry.Value;
images.Add( Don't know what will be here? );
}
var comboBox = sender as ComboBox;
comboBox.ItemsSource = images;
and my XAML
<ComboBox HorizontalAlignment="Left" Grid.Column="0" Grid.Row="0" VerticalAlignment="Top" Width="320" Loaded="ComboBox_Loaded" SelectionChanged="ComboBox_SelectionChanged"/>
It's the easiest to use an item template. To do so we define a DataTemplate with DataType String and set it to the ComboBox.ItemTemplate. In order to use String in XAML we need to reference the xmlns:system="clr-namespace:System;assembly=mscorlib" assembly and namespace. For the binding we use a ObservableCollection<string> that holds the relative paths to your images:
View model:
public class ViewModel : INotifyPropertyChanged
{
public TestViewModel()
{
this.ImageSources = new ObservableCollection<string>() { #"Resources\Icons\image.png" };
}
/// <summary>
/// Event fired whenever a child property changes its value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Method called to fire a <see cref="PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName"> The property name. </param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<string> imageSources;
public ObservableCollection<string> ImageSources
{
get => this.imageSources;
set
{
this.imageSources = value;
OnPropertyChanged();
}
}
}
Xaml:
<Window x:Class="MainWindow"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<Window.DataContext>
<viewModels:ViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="ComboBoxItemTemplate" DataType="system:String">
<Image Source="{Binding}" Height="100" Width="100"/>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<ComboBox ItemTemplate="{StaticResource ComboBoxItemTemplate}"
ItemsSource="{Binding ImageSources}" />
</StackPanel>
</Grid>
</Window>
To make it work, your dictionary should contain the relative image paths. If not you have to convert. So instead of initializing the ObservableCollection in the constructor, like in the example, you can move the initialization to anywhere else.
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;
}
}
Hi im working on an Wpf MVVM project, but i can't figure it out how to bind a button with the Command attribute in Xaml to a RelayCommand in the Viewmodel, i found multiple answer's online but i don't get it (implementing the ICommand interface and canexecute and stuff..) , the thing is that i have an already created project made by other developers where they simply , bind the button in Xaml like so :
heres the full code in the View :
<Window x:Class="MvvmLight3.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:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:mvvm="http://www.galasoft.ch/mvvmlight"
xmlns:ignore="http://www.galasoft.ch/ignore"
mc:Ignorable="d ignore"
Height="300"
Width="300"
Title="MVVM Light Application"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<TextBlock FontSize="36"
FontWeight="Bold"
Foreground="Purple"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" ><Run Text="WelcomeTitle"/><InlineUIContainer>
<Label Content="{Binding welcome}"/>
</InlineUIContainer></TextBlock>
<Button Content="Button" Command="{Binding The_Command}" HorizontalAlignment="Left" Margin="170,80,0,0" VerticalAlignment="Top" Width="75">
</Button>
</Grid>
and in the ViewModel the full code is :
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
using MvvmLight3.Model;
using System.Windows.Input;
namespace MvvmLight3.ViewModel
{
/// <summary>
/// This class contains properties that the main View can data bind to.
/// <para>
/// See http://www.mvvmlight.net
/// </para>
/// </summary>
public class MainViewModel : ViewModelBase
{
private readonly IDataService _dataService;
/// <summary>
/// The <see cref="WelcomeTitle" /> property's name.
/// </summary>
public const string WelcomeTitlePropertyName = "WelcomeTitle";
private string _welcomeTitle = string.Empty;
private string _welcome = "this work";
/// <summary>
/// Gets the WelcomeTitle property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string WelcomeTitle
{
get
{
return _welcomeTitle;
}
set
{
Set(ref _welcomeTitle, value);
}
}
public string welcome
{
get
{
return _welcome;
}
set
{
Set(ref _welcome, value);
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel(IDataService dataService)
{
_dataService = dataService;
_dataService.GetData(
(item, error) =>
{
if (error != null)
{
// Report error here
return;
}
WelcomeTitle = item.Title;
});
}
private RelayCommand _The_Command;
public RelayCommand The_Command
{
get
{
return _The_Command
?? (_The_Command = new RelayCommand(
() =>
{
//some Code
}));
}
}
////public override void Cleanup()
////{
//// // Clean up if needed
//// base.Cleanup();
////}
}
}
in my case the execution dont enter the RelayCommand.
the code in the ViewModelLocator.cs
/*
In App.xaml:
<Application.Resources>
<vm:ViewModelLocatorTemplate xmlns:vm="clr-namespace:MvvmLight3.ViewModel"
x:Key="Locator" />
</Application.Resources>
In the View:
DataContext="{Binding Source={StaticResource Locator}, Path=ViewModelName}"
*/
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using MvvmLight3.Model;
namespace MvvmLight3.ViewModel
{
/// <summary>
/// This class contains static references to all the view models in the
/// application and provides an entry point for the bindings.
/// <para>
/// See http://www.mvvmlight.net
/// </para>
/// </summary>
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
/// <summary>
/// Cleans up all the resources.
/// </summary>
public static void Cleanup()
{
}
}
}
the project is MVVMLight (wpf451) template .
Thank you.
It worked, it's something silly, actually if you notice my relay Command (The_Command) was empty and although it had a comment inside it (//some Code), a breakpoint on the get or return never went through when I click the button.
Solution:
after I added a functioning code inside the Command it worked: added for example a simple MessageBox.Show. so the Binding was correct. For the info I'm using the VisualStudio Enterprise 2017 .
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>