Textbox binding with trigger - c#

I need to bind the text box with the data available on it and execute a command associate with that. I want the data entered as well command execution only when "Enter" button on keyboard is pressed. I used to the below code, but it seems, I am getting command execution without "Enter" is pressed also found that for each number or text pressed, I am getting the command. I don't want this to happen.
my InputDataTemplate.xaml code:
<Label Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center" Content="{Binding Name}" />
<Label Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center" Content="{Binding Value}" />
<TextBox Grid.Column="1" Width="60" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Data}" DataContext="{Binding}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged" >
<ei:CallMethodAction TargetObject="{Binding}" MethodName="IpDataTrig" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
I can understand EventName="TextChanged" plays a role here. But not sure about the other stuffs.
My TesterView.xaml code:
<UserControl.Resources>
<DataTemplate x:Key="InputDataTemplate" >
<local:InputDataTemplate DataContext="{Binding}" />
</DataTemplate>
</UserControl.Resources>
<Grid Grid.Row="2" Background="AliceBlue" >
<Label Content="Input Datas" FontWeight="Bold"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<Border Grid.Row="3" BorderBrush="Black" BorderThickness="0,2,0,0" >
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled" >
<ItemsControl x:Name="IpDataNames"
DataContext="{Binding }"
ItemsSource="{Binding IpDataNames}"
ItemTemplate="{DynamicResource InputDataTemplate}" />
</ScrollViewer>
</Border>
my TesterViewModel.cs:
private ObservableCollection<InputDataService> _IpDataNames;
private InputDataService l_IpDataNames;
_IpDataNames = new ObservableCollection<InputDataService>();
public ObservableCollection<InputDataService> IpDataNames
{
get { return _IpDataNames; }
set
{
IpDataNames = value;
}
}
InputDataService.cs:
public class InputDataService : BindableBase
{
public string Name { get; set; }
public string Value { get; set; }
public string Data { get; set; }
public void IpDataTrig()
{
Debug.WriteLine(string.Format("\nInput Data {0} Updated : {1} : {2}", Name, Data, Value));
}
}

Possible duplicate question: https://stackoverflow.com/a/10466285/475727
BTW, nothing is wrong about capturing KeyPress event and calling command from codebehind. It is not violation of MVVM pattern.
Sometimes I use my own behavior implemented as attached property. Big advantage is, that I can use it in styles.
This behaviour update binding source on text property and then calls command. (TextBox.Text binding is updated on losf focus by default)
public static class TextBoxBehaviour
{
public static readonly DependencyProperty CommandOnEnterPressedProperty = DependencyProperty.RegisterAttached("CommandOnEnterPressed",typeof (ICommand),typeof (TextBoxBehaviour),
new FrameworkPropertyMetadata(CommandOnEnterPressedPropertyChanged));
private static void CommandOnEnterPressedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var sender = (TextBox) d;
sender.KeyDown -= OnKeyDown;
if (e.NewValue is ICommand)
{
sender.KeyDown += OnKeyDown;
}
}
private static void OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
var tbx = (TextBox) sender;
var textBindigExpression = tbx.GetBindingExpression(TextBox.TextProperty);
if (textBindigExpression != null)
{
textBindigExpression.UpdateSource();
}
var command = GetCommandOnEnterPressed(tbx);
if (command.CanExecute(null)) command.Execute(null);
}
}
[AttachedPropertyBrowsableForType(typeof(TextBox))]
public static void SetCommandOnEnterPressed(TextBox elementName, ICommand value)
{
elementName.SetValue(CommandOnEnterPressedProperty, value);
}
public static ICommand GetCommandOnEnterPressed(TextBox elementName)
{
return (ICommand) elementName.GetValue(CommandOnEnterPressedProperty);
}
}
and usage
<TextBox Text="{Binding SearchTerm, UpdateSourceTrigger=Explicit}"
my:TextBoxBehaviour.CommandOnEnterPressed="{Binding SearchCommand}"/>

Related

How to Create a Filter in WPF using Datagrid and ComboBox

So here I have a MVVM form. the Form contains a Datagrid which is connected to the Databank. I also have a ComboBox which I want to use as a filter option. The Filter option shoud filter by the "AnlV nr" so when the user selects "01" from the ComboBox the datagrid should refresh and show only that "AnlV nr" that have "01" Below I will share you the code and you can see that i've gotten as far as showing the "AnlV" values in the ComboBox but I now do not know how to do the rest and make the filter work. Below is my Viewmodel and the Xaml code.
If anyone can help me with this I would really apreciate it.
Xaml Code:
<Window x:Class="QBondsFrontend.Views.Input.AnlVTexteView"
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:QBondsFrontend.Views.Input" xmlns:input="clr-namespace:QBondsFrontend.ViewModels.Input" d:DataContext="{d:DesignInstance Type=input:AnlVTexteViewModel}"
mc:Ignorable="d"
Title="AnlVTexteView"
Width="800"
MinHeight="400"
Height="490"
MinWidth="1010"
MaxWidth="1010"
UseLayoutRounding="True">
<Grid Background="#A8A8A8" >
<StackPanel VerticalAlignment="Top" Background="#A8A8A8" Orientation="Horizontal" Height="57">
<Label
Content="AnlV Nr.:" Height="35" FontSize="12"/>
<ComboBox Background="LightGray" Height="20" Width="70" ItemsSource="{Binding lstAnlVTexte}" SelectedItem="{Binding Search}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding AnlVPara}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Height="18" Width="68" Margin="5, 0"
Content="Filter löschen" FontSize="11" Style="{StaticResource STL_ButtonStandard}"
x:Name="filterlöschen"
Command="{Binding icdFilterDelete}"/>
</StackPanel>
<StackPanel Background="LightGray" VerticalAlignment="Top" Height="177" Margin="0,57,0,0">
<DataGrid x:Name="datagridXAML"
Height="177"
ItemsSource="{Binding lstAnlVTexte, Mode=TwoWay}"
Style="{StaticResource STL_DataGridReporting}"
CellStyle="{StaticResource STL_DataGridCellReporting}"
ColumnHeaderStyle="{StaticResource STL_DataGridColumnHeaderReporting}"
AlternatingRowBackground="#A8A8A8"
CanUserResizeColumns="False"
>
<DataGrid.Columns>
<DataGridTextColumn Header="AnlV-Nr"
Binding="{Binding AnlVPara}"
Width="60"/>
<DataGridTextColumn Header="gültig ab"
Binding="{Binding TextGueltigAb}"
Width="68"/>
<DataGridTextColumn Header="Text"
Binding="{Binding ParaText}"
Width="750"/>
<DataGridTextColumn Header="Info"
Binding="{Binding Info}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<StackPanel Background="#A8A8A8" HorizontalAlignment="Center" Margin="10,268,0,141" Width="1010" >
<Label Content="Bearbeitungsbereich" FontWeight="Bold" FontSize="12" Height="33" />
</StackPanel>
<StackPanel>
<StackPanel Orientation="Horizontal" Background="#A8A8A8" HorizontalAlignment="Center"
Width="1010" Margin="0,294,0,0" Height="31">
<Label Height="25" Width="60" Margin="20, 0, 0, 0" Content="AnlV-Nr.:" />
<ComboBox IsEditable="True" Background="gray" Height="22" Width="69" ItemsSource="{Binding AnlVPara}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding lstAnlVTexte}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<CheckBox Height="15" Margin="10, 0, 0, 0" />
<Label Height="26" Width="122" Content="Editierwarnungen" />
<StackPanel Height="48" Width="100"/>
</StackPanel>
<StackPanel Height="22" Orientation="Horizontal">
<Label Margin="20, 0, 0, 0" Content="gültig ab:" Height="27" />
<TextBox Background="LightGray" Height="20" Width="100" />
</StackPanel>
<StackPanel Height="50" Orientation="Horizontal">
<Label Content="Text:" Height="27" Width="38" Margin="42,0,0,10" />
<TextBox Background="LightGray" Width="500" Height="43" />
</StackPanel>
<StackPanel Orientation="Horizontal" >
<Label Content="Info:" Height="27" Width="38" Margin="42,0,0,0" />
<TextBox Background="LightGray" Width="500" Height="20" />
<Button x:Name="BTN_speichern" Width="80" Height="18" Margin="20,0,0,0" Content="Speichern"
Style="{StaticResource STL_ButtonStandard}" Command="{Binding icdSpeichern}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
ViewModel:
using Newtonsoft.Json;
using QBondsData.DBModels;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Linq;
namespace QBondsFrontend.ViewModels.Input
{
public class AnlVTexteViewModel : BaseViewModel
{
#region data
private string _AnlVPara;
private DateTime _TextGueltigAb;
private string _ParaText;
private string _Info;
private List<AnlVhistText> _lstAnlVTexte;
private string _search;
#endregion
#region constructor
public AnlVTexteViewModel()
{
icdFilterDelete = new RelayCommand<object>(parameter => filterdelete(), parameter => true);
icdSpeichern = new RelayCommand<object>(parameter => speichern(), parameter => true);
GetData();
}
#endregion
#region members
public ICommand icdFilterDelete { get; set; }
public ICommand icdSpeichern { get; set; }
private string Search
{
get { return _search; }
set
{
_search = value;
OnPropertyChanged("Search");
}
}
public string AnlVPara
{
get
{
return _AnlVPara;
}
set
{
_AnlVPara = value;
OnPropertyChanged("AnlVPara");
}
}
public DateTime TextGueltigAb
{
get
{
return _TextGueltigAb;
}
set
{
_TextGueltigAb = value;
OnPropertyChanged("TextGueltigAb");
}
}
public string ParaText
{
get
{
return _ParaText;
}
set
{
_ParaText = value;
OnPropertyChanged("ParaText");
}
}
public string Info
{
get
{
return _Info;
}
set
{
_Info = value;
OnPropertyChanged("Info");
}
}
public List<AnlVhistText> lstAnlVTexte
{
get { return _lstAnlVTexte; }
set
{
_lstAnlVTexte = value;
OnPropertyChanged("lstAnlVTexte");
}
}
#endregion
#region methods
private void filterdelete()
{
}
private void speichern()
{
}
private async Task GetData()
{
HttpResponseMessage Response = await Globals.SendRequest("AnlVTexte/GetAnlVTexte"
, "GET");
if (Response.IsSuccessStatusCode)
{
lstAnlVTexte = JsonConvert.DeserializeObject<List<AnlVhistText>>
(await Response.Content.ReadAsStringAsync());
}
else
{
lstAnlVTexte = new List<AnlVhistText>();
Application.Current.Dispatcher.Invoke((Action)delegate
{
Globals.CloseReportByHash(this.GetHashCode()
, "Fehler! (HTTP Status " + Response.StatusCode + ")." +
"Kontaktieren Sie den Support.");
});
}
}
#endregion
}
}
When you change the type of lstAnlVTexte to ICollectionView you get two events CurrentChanged and CurrentChanging which you can handle in your viewmodel. From the ICollectionView you can get the CurrentItem.
Like this:
private List<AnlVhistText> _anlVTexte = new List<AnlVhistText>();
public AnlVTexteViewModel()
{
[...]
lstAnlVTexte = new CollectionView(_anlVTexte);
lstAnlVTexte.CurrentChanged += SelectionChanged; // raised after the current item has been changed.
lstAnlVTexte.CurrentChanging += SelectionChanging; // raise before changing the current item. Event handler can cancel this event.
}
private void SelectionChanged(object sender, EventArgs e)
{
var selectedItem = lstAnlVTexte.CurrentItem;
}
private void SelectionChanging(object sender, CurrentChangingEventArgs e)
{
}
private ICollectionView _lstAnlVTexte;
public ICollectionView lstAnlVTexte
{
get { return _lstAnlVTexte; }
set
{
_lstAnlVTexte = value;
OnPropertyChanged("lstAnlVTexte");
}
}
Here's a sample using the community toolkit mvvm and linq.
If you're not familiar, the toolkit does code generation.
This is a simple scenario to illustrate the approach.
Mainwindowviewmodel.
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private int selectedFilterInt = -1;
partial void OnSelectedFilterIntChanged(int newValue)
{
FilteredList = new ObservableCollection<MyItem>(FullList.Where(x=>x.Category == selectedFilterInt).ToList());
}
public List<int> FilterOptions { get; set; } = new List<int> {1,2,3};
private List<MyItem> FullList= new List<MyItem>
{
new MyItem{ Category = 1},
new MyItem{ Category = 1},
new MyItem { Category = 1 },
new MyItem { Category = 2 },
new MyItem { Category = 2 },
new MyItem { Category = 3 }
};
[ObservableProperty]
private ObservableCollection<MyItem> filteredList = new ObservableCollection<MyItem>();
public MainWindowViewModel()
{
FilteredList = new ObservableCollection<MyItem>(FullList);
}
}
There's a full list of all the items.
But a filtered list is going to be bound to the itemssource of my listbox ( equivalent to your datagrid ).
Due to the code generated, when selectedFilterInt changes, OnSelectedFilterIntChanged will be called. It's got a handler listening for property changed of SelectedFilterInt if you dug into the generated code.
That method uses a linq where to filter the full list into filtered list.
Setting that filtered list property raises property changed and the view re reads the new collection.
MainWindow. ( I did mention this is simplified )
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<ComboBox SelectedItem="{Binding SelectedFilterInt}"
ItemsSource="{Binding FilterOptions}"/>
<ListBox ItemsSource="{Binding FilteredList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Category}"/>
<TextBlock Text="{Binding Comment}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
</Window>
and MyItem
public partial class MyItem : ObservableObject
{
[ObservableProperty]
private int category = 0;
[ObservableProperty]
private string comment = "Some test string";
}
Which is a bit underwhelming visually but works:
In your code you need to get all the data into a collection.
Call that FulList.
You then need another collection which will be the filtered data.
Call this FilteredList.
Bind itemssource to FilteredList
Initially, you presumably want FilteredList to be = FullList
Then when the user selects something in the combobox you need to react to that.
You could bind selecteditem to a property and act in the setter or handle propertychanged like my code does.
However you do it, you get a new integer.
You then use that to filter FullList into a new collection which will replace the bound FilteredList.
You also need to somehow have one entry per AnlV nr whatever that is in your combobox.
AnlV nr isn't going to work as a property name since it's got a space but it is the equivalent to Category in my sample.
You will use that selected value in the linq.
Substitute the name of that property for Category. Substitute the type of whatever your collection is. Maybe that's AnlVhistText. I'm not sure.

DataGrid is updating itself in realtime and holding unsaved changes

I have a problem with DataGrid and after week trying to solve this I am out of mind.
I am using Prism.MVVM to handle loading properties, INotifyPropertyChanged etc.
My datagrid is being populated from database (EF) by of course ViewModel. When I double click on the row edit window will open with populated fields etc. I am doing this by "SelectedItem". Everything to this moment is working fine, but:
• When I editing my "Stock" textbox I see in the ProductListView window that this value is changing in realtime and even if I hit Cancel (and the window closed) it stays as I left it in ProductView and even after opening edit window again "Stock" value remain wrong, but in database the value is correct.
• When I edit for example "Category" or "Name" I do not see changes in datagrid in realtime (in this case values in DataGrid stay correct), but if I hit Cancel (and the window closed) an reopen edit window again value remain wrong, but in database the value is correct.
I tried to DeepCopy it and then Override SelectedItem back after edit and it work for database (it getting updated), but view (DataGrid) does not.
ProductListView:
<DataGrid ColumnWidth="Auto" Grid.Row="1" ItemsSource="{Binding ProductsCollectionView, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedItem}" AutoGenerateColumns="False" IsReadOnly="True"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Auto">
Methods/Commands responsible for edit in ProductListViewModel:
private Product _selectedItem;
public Product SelectedItem
{
get => _selectedItem;
set => SetProperty(ref _selectedItem, value);
}
public ProductListViewModel() // Contructor
{
_service = new ProductService();
Load();
ProductsCollectionView = CollectionViewSource.GetDefaultView(Data);
ProductsCollectionView.Filter = FilterProducts;
LoadCommands();
EditCommand = new DelegateCommand(Edit);
}
public ICommand EditCommand { get; set; }
private void Edit()
{
var dialog = new ProductView(SelectedItem);
dialog.ShowDialog();
if (dialog.DialogResult == true)
{
_service.UpdateProductData(SelectedItem);
_service.SaveChanges();
}
}
ProductView (Btw. I am using same View for Adding and Editing products, that is why I passing "SeleectedItem through the constructor)
<StackPanel Margin="0,0,13,0" Grid.Column="0">
<TextBlock Text="Product type" Style="{StaticResource StackPanelTextBox}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="41*" />
<ColumnDefinition Width="251*"/>
<ColumnDefinition Width="31*" />
</Grid.ColumnDefinitions>
<ComboBox IsEditable="True" IsTextSearchEnabled="True" ItemsSource="{Binding ProductTypes}" DisplayMemberPath="Name" SelectedValuePath="ProductTypeId" SelectedValue="{Binding Data.ProductTypeId, UpdateSourceTrigger=PropertyChanged}" Grid.ColumnSpan="2"/>
<Button Grid.Column="1" Command="{Binding AddProductTypeCommand}" FontSize="12" Padding="0" Height="15" Background="Transparent" Grid.ColumnSpan="2" Margin="251,6,0,6"/>
</Grid>
</StackPanel>
<StackPanel Margin="0,0,13,0" Grid.Column="1">
<TextBlock Text="Signature" Style="{StaticResource StackPanelTextBox}"/>
<TextBox Text="{Binding Data.Signature}"/>
</StackPanel>
<StackPanel Margin="0,0,0,0" Grid.Column="2">
<TextBlock Text="EAN" Style="{StaticResource StackPanelTextBox}"/>
<TextBox MaxLength="13" Text="{Binding Data.Ean}"/>
</StackPanel>
<Grid Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80*" />
<ColumnDefinition Width="20*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0" Margin="0,7,13,10">
<TextBlock Text="Name" Style="{StaticResource StackPanelTextBox}"/>
<TextBox x:Name="tbName" Text="{Binding Data.Name}"/>
</StackPanel>
<StackPanel Grid.Column="1" Grid.Row="0" Margin="0,0,0,0" VerticalAlignment="Center">
<TextBlock Text="Stock"/>
<mah:NumericUpDown Margin="0,3,0,3" Minimum="0" Interval="1" Value="{Binding Data.InStock}" />
</StackPanel>
</Grid>
ProductView.xaml.cs
public ProductView(Product product)
{
InitializeComponent();
var viewModel = new ProductViewModel(product);
WindowStartupLocation = WindowStartupLocation.CenterScreen;
viewModel.SaveAction = () =>
{
DialogResult = true;
};
viewModel.CancelAction = () =>
{
DialogResult = false;
};
DataContext = viewModel;
}
ProductViewModel
public Action SaveAction { get; set; }
public Action CancelAction { get; set; }
public ICommand Save { get; set; }
public ICommand Cancel { get; set; }
private Product _data;
public Product Data
{
get => _data;
set => SetProperty(ref _data, value);
}
public ProductViewModel(Product data)
{
LoadSellers();
LoadProductTypes();
LoadPackages();
Data = data;
NettoPrice = Data.PurchasePrice;
Save = new DelegateCommand(() => SaveAction?.Invoke());
Cancel = new DelegateCommand(() => CancelAction?.Invoke());
HideData = new DelegateCommand(HideMethod);
AddProductTypeCommand = new DelegateCommand(AddProductType);
EyeColor = #"..\Resources\Images\Eye-grey-48.png";
}
You need an extra data layer if you want to implement edit and cancel behavior of a data model. You can do this by implementing the IEditableObject interface.
You should never open the dialog from the view model. Instead, open it from a Button.Click handler in the code-behind. Define a DataTemplate for the dialog and assign it to the Window.ContentTemplate property. Also make sure that the database handling is implemented inside the model.
The following example shows how to display a reusable EditDialog (that operates on IEditableObject implementations) from the view. The example also shows how to cancel or commit data changes to the data model.
App.xaml
<DataTemplate DataType="{x:Type local:Product}">
<TextBox Text="{Binding Signature}" />
</DataTemplate>
EditDialog.xaml
Reusable dialog. Simply define a DataTemplate for the Window.ContentTemplate property to change the hosted Content.
Assign the data to be edited to the EditDialog.DataContext. This dialog can host any data that implements IEditableObject.
<Window Content="{Binding}">
<Window.Template>
<ControlTemplate TargetType="Window">
<Grid Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<AdornerDecorator Grid.Row="0">
<ContentPresenter />
</AdornerDecorator>
<!-- Dialog chrome -->
<Grid Grid.Row="1">
<StackPanel Orientation="Horizontal">
<Button Content="Commit"
Click="OnOkButtonClicked" />
<Button Content="Cancel"
Click="OnCancelButtonClicked"
IsCancel="True"
IsDefault="True" />
</StackPanel>
<ResizeGrip x:Name="WindowResizeGrip"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Visibility="Collapsed"
IsTabStop="false" />
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ResizeMode"
Value="CanResizeWithGrip">
<Setter TargetName="WindowResizeGrip"
Property="Visibility"
Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Template>
</Window>
EditDialog.xaml.cs
public partial class EditDialog : Window
{
public EditDialog()
{
InitializeComponent();
this.Loaded += OnLoaded;
}
// In case the Window is closed using the chrome button
protected override void OnClosing(CancelEventArgs e)
{
if (this.Content is IEditableObject editableObject)
{
editableObject.CancelEdit();
}
base.OnClosing(e);
}
private void OnLoaded(object sender, EventArgs e)
{
// Content is set via data binding
if (this.Content is IEditableObject editableObject)
{
editableObject.BeginEdit();
}
}
private void OnCancelButtonClicked(object sender, RoutedEventArgs e)
{
if (this.Content is IEditableObject editableObject)
{
editableObject.CancelEdit();
}
this.Close();
}
private void OnOkButtonClicked(object sender, RoutedEventArgs e)
{
if (this.Content is IEditableObject editableObject)
{
editableObject.EndEdit();
}
this.Close();
}
}
MainWindow.xaml
<DataGrid ItemsSource="{Binding ProductsCollectionView}"
SelectedItem="{Binding SelectedItem}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<EventSetter Event="MouseDoubleClick"
Handler="DataGridRow_MouseDoubleClick" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
MainWindow.xaml.cs
partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ProductListViewModel();
}
private void DataGridRow_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var productListViewModel = this.DataContext as ProductListViewModel;
// Alternatively, create a e.g., EditItem dependency property
// and bind it to the DataGrid.SelectedItem
Product editItem = productListViewModel.SelectedItem;
var editDialog = new EditDialog()
{
DataContext = editItem
};
editDialog.ShowDialog();
}
}
Product.cs
class Product : INotifyPropertyChanged, IEditableObject
{
internal class ProductData
{
// Use object.MemberwiseClone to create a shallow copy
public ProductData Clone() => MemberwiseClone() as ProductData;
public string Signature { get; set; }
}
public Product()
{
this.EditData = new ProductData();
this.BackupData = new ProductData();
}
public void BeginEdit()
{
if (this.IsInEditMode)
{
// Consider to throw an exception
return;
}
// Creates a shallow copy.
// If required, use a copy constructor to create a deep copy.
this.BackupData = this.EditData.Clone();
this.IsInEditMode = true;
}
public void CancelEdit()
{
if (!this.IsInEditMode)
{
// Consider to throw an exception
return;
}
this.EditData = this.BackupData;
this.IsInEditMode = false;
// Raise change notification for all public properties
// to undo potential binding changes
OnPropertyChanged(null);
}
public void EndEdit()
{
this.IsInEditMode = false;
}
public string Signature
{
get => this.EditData.Signature;
set
{
this.EditData.Signature = value;
OnPropertyChanged();
}
}
public bool IsInEditMode { get; private set; }
private ProductData BackupData { get; set; }
private ProductData EditData { get; set; }
}

RadioButton IsChecked not binding to separate variables

I have two radio buttons in a group as part of my XAML project:
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Center" Orientation="Horizontal" Grid.Column="0" Margin="20,0,0,0">
<RadioButton x:Name="XMLViewButton" GroupName="DisplayType" IsChecked="{Binding XmlViewIsChecked, FallbackValue=True, Mode=TwoWay}" Content="XML View" Margin="0,0,5,0"/>
<RadioButton x:Name="TextViewButton" GroupName="DisplayType" IsChecked="{Binding TextViewIsChecked, FallbackValue=False, Mode=TwoWay}" Content="Text View" Margin="5,0,0,0"/>
</StackPanel>
And I then have a command later on which refers to these IsChecked bindings:
public void CopyToClipboard(object o)
{
if (TextViewIsSelected == true)
{
Clipboard.SetText(myFile.TextContent);
}
else if (XmlViewIsSelected == true)
{
Clipboard.SetText(myFile.XMLContent);
}
}
However, the XmlViewIsSelected is permanently True and TextViewIsSelected is always false, no matter which radio button is selected. What am I missing?
I think you are mispelling XmlViewIsChecked with XmlViewIsSelected
The following for me is working
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Center" Orientation="Horizontal" Grid.Column="0" Margin="20,0,0,0">
<RadioButton x:Name="XMLViewButton" GroupName="DisplayType" IsChecked="{Binding XmlViewIsChecked, FallbackValue=True, Mode=TwoWay}" Content="XML View" Margin="0,0,5,0"/>
<RadioButton x:Name="TextViewButton" GroupName="DisplayType" IsChecked="{Binding TextViewIsChecked, FallbackValue=False, Mode=TwoWay}" Content="Text View" Margin="5,0,0,0"/>
<Button Content="Check" Click="Button_Click" />
</StackPanel>
public partial class MainWindow : Window
{
public bool XmlViewIsChecked { get; set; }
public bool TextViewIsChecked { get; set; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (TextViewIsChecked)
{
Clipboard.SetText("text");
}
else if (XmlViewIsChecked)
{
Clipboard.SetText("xml");
}
}
}

combobox binding MVVM C#

Im missing something but i have no idea what :(
I've bind IEnumerable collection to combobox. I would like to use its selected value as parameter for shutdown command, however when i press start button it doesn't load selected values.
I've followed few tutorials to understand MVVM but there is still something missing but i cant figured out what.
Here is MainWindow.xaml :
<grid>
<StackPanel>
<!--Title label-->
<TextBlock Text="Wyłącz komputer za:" Margin="5"/>
<!-- Blocks used to set hours and minutes-->
<StackPanel Orientation="Horizontal">
<TextBlock Text="Godziny:" Margin="5"/>
<ComboBox x:Name="HoursCB" Margin="5" Width="40" ItemsSource="{Binding myHours}" SelectedValue="{Binding selectedHours, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Minuty:" Margin="5" />
<ComboBox x:Name="MinutesCB" Margin="5" Width="40" ItemsSource="{Binding myMinutes}" SelectedValue="{Binding selectedMinutes, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel >
<!-- Timer -->
<StackPanel Orientation="Horizontal">
<Label x:Name="HHLabel" Content="{Binding selectedHours}" FontSize="30" HorizontalAlignment="Center" Width="45"/>
<Label x:Name="Colon1" Content=":" FontSize="30" HorizontalAlignment="Center" Width="25"/>
<Label x:Name="MMLabel" Content="{Binding selectedMinutes}" FontSize="30" HorizontalAlignment="Center" Width="45"/>
<Label x:Name="Colon2" Content=":" FontSize="30" HorizontalAlignment="Center" Width="25"/>
<Label x:Name="SSLabel" Content="00" FontSize="30" HorizontalAlignment="Right" Width="45"/>
</StackPanel>
<!-- Start Button -->
<Button Content="uruchom odliczanie" Margin="5" Command="{Binding StartCommand}" />
<!-- Stop Button-->
<Button Content="Zatrzymaj odliczanie" Margin="5" Command="{Binding StopCommand}"/>
</StackPanel>
</grid>
and here is view model :
class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
StartCommand = new AddNameCommand(this);
}
class AddNameCommand : ICommand
{
MainWindowViewModel parent;
public AddNameCommand(MainWindowViewModel parent)
{
this.parent = parent;
parent.PropertyChanged += delegate { CanExecuteChanged?.Invoke(this, EventArgs.Empty); };
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{ return true; }
public void Execute(object parameter)
{
int num = parent.SelectedHours * 3600 + parent.SelectedMinutes * 60;
MessageBox.Show($"Shutting down the computer in {num} !");
//Process.Start("shutdown", string.Format("/s /t {0}", num));
}
}
public ICommand StartCommand { get; private set; }
/// <summary>
/// Combobox Items.
/// </summary>
//public IEnumerable<int> myHours = Enumerable.Range(0, 23);
//public IEnumerable<int> myMinutes = Enumerable.Range(1, 59);
public ObservableCollection<int> myHours { get; set; } = new ObservableCollection<int>(Enumerable.Range(0, 23));
public ObservableCollection<int> myMinutes { get; set; } = new ObservableCollection<int>(Enumerable.Range(1, 59));
/// <summary>
/// Selected time properties.
/// </summary>
public int SelectedMinutes
{
get { return mSelectedMinutes; }
set
{
if (value == mSelectedMinutes)
return;
mSelectedMinutes = value;
OnPropertyChanged();
}
}
int mSelectedMinutes;
public int SelectedHours
{
get { return mSelectedHours; }
set
{
if (value == mSelectedHours)
return;
mSelectedHours = value;
OnPropertyChanged();
}
}
int mSelectedHours;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName]string propertyName = null)
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
}
You should change your xaml to the following (note the case sensitive SelectedHours/Minutes):
<!-- Blocks used to set hours and minutes-->
<StackPanel Orientation="Horizontal">
<TextBlock Text="Godziny:" Margin="5"/>
<ComboBox x:Name="HoursCB" Margin="5" Width="40" ItemsSource="{Binding myHours}" SelectedValue="{Binding SelectedHours, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Minuty:" Margin="5" />
<ComboBox x:Name="MinutesCB" Margin="5" Width="40" ItemsSource="{Binding myMinutes}" SelectedValue="{Binding SelectedMinutes, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel >
Binding errors can be easily detected by opening the Output Window in Visual Studio and looking for the following:
System.Windows.Data Error: 40 : BindingExpression path error: 'selectedHours' property not found on 'object' ''MainWindowViewModel' (HashCode=46431654)'. BindingExpression:Path=selectedHours; DataItem='MainWindowViewModel' (HashCode=46431654); target element is 'ComboBox' (Name='HoursCB'); target property is 'SelectedValue' (type 'Object')
You have not binded the Selected Values of combo boxes to the right property in the View Model. Just change "selectedHours" to "SelectedHours" and "selectedMinutes" to "SelectedMinutes" in your XAML to bind it properly.
You code is behaving like i expedted it. Your problem is the Enumerable.Range function. It starts with an inclusive 0 and then counts up 23 times Inclunding 0

ListBox item from another ListBox

On my Windows Phone app, i have two ListBox. I need that when keep an item pressed (from ListBox1), the item populates the ListBox2.
So, my LisBox1 is populated from JSON (binding).
The code bellow doesn't works (Error: Value does not fall within the expected range.):
public void addToList2(object sender, System.Windows.Input.GestureEventArgs e)
{
var dcs = ((FrameworkElement)sender).DataContext;
Fields fi = (Fields)dcs;
List2.Items.Add(fi);
}
ListBoxes:
<ListBox Name="List1" Hold="addToList2" ItemsSource="{Binding Items}" Margin="0,85,0,0" >
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="242" />
<ColumnDefinition Width="128" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Margin="0,0,-62,17" Grid.ColumnSpan="3">
<StackPanel.Background>
<SolidColorBrush Color="#FF858585" Opacity="0.5"/>
</StackPanel.Background>
<TextBlock x:Name="NameTxt" Grid.Column="0" Text="{Binding descricao}" TextWrapping="Wrap" FontSize="20" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Grid.Column="1" Text="{Binding valor_preco_a, StringFormat=N2}" TextWrapping="Wrap" Margin="45,20,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
<TextBlock Grid.Column="3" Text="{Binding codigo}" TextWrapping="Wrap" FontSize="35" Margin="370,-50,12,0" Style="{StaticResource PhoneTextNormalStyle}" Foreground="Blue"/>
</StackPanel>
<TextBlock Grid.Column="0" Text="R$" Margin="15,48,158,17" Style="{StaticResource PhoneTextSubtleStyle}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<ListBox Name="List2" HorizontalContentAlignment="Stretch" Grid.ColumnSpan="3" Margin="0,182,-66,0" Visibility="Collapsed">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Height="100">
<StackPanel.Background>
<SolidColorBrush Color="#FFE8FF00" Opacity="0.2"/>
</StackPanel.Background>
<TextBlock Grid.Column="0" Text="{Binding descricao}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<Listbox x:Name="listbox1" ItemsSource="{Binding listOneObjects}"
SelectedItem="{listOneSelectedItem}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="PressAndHold">
<i:InvokeCommandAction Command="{Binding TouchDownCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Listbox>
<Listbox x:Name="listbox2" ItemsSource="{Binding listTwoObjects, Mode=TwoWay}"
SelectedItem="{Binding listTwoSelectedItem, Mode=TwoWay}">
</Listbox>
Your C#
using System.ComponentModel;
public class YourClassName : INotifyPropertyChanged
private List<Object> _listOneObjects;
public List<Object> listOneObjects
{
get{ return _listOneObjects; }
set{ _listOneObjects = value; OnPropertyChanged("listOneObjects"); }
}
private Object _listOneSelectedItem;
public Object listOneSelectedItem
{
get{ return _listOneSelectedItem; }
set{ _listOneSelectedItem = value; OnPropertyChanged("listOneSelectedItem"); }
}
private List<Object> _listTwoObjects ;
public List<Object> listTwoObjects
{
get{ return _listOneObjects; }
set{ _listOneObjects = value; OnPropertyChanged("listTwoObjects "); }
}
private Object _listTwoSelectedItem
public Object listTwoSelectedItem
{
get{ return _listTwoSelectedItem; }
set{ _listTwoSelectedItem= value; OnPropertyChanged("listTwoSelectedItem"); }
}
public ICommand TouchDownCommand{ get{ return _TouchDownCommand; }
private _TouchDownCommand;
public YourClassName(){
this._TouchDownCommand= new ActionCommand(TouchDownExecuted);
}
private void TouchDownExecuted(){
listTwoObjects.clear();
listTwoObjects.Add(listOneSelectedItem);
OnPropertyChanged("listTwoObjects");
}
PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(String prop){
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
There are a number of moving parts here so you'll need to know the following.
Databinding
Command Binding
Interaction Triggers
Event Triggers
Binding Modes
object myItem;
public Form1()
{
InitializeComponent();
listBox1.Items.Add("Paul");
listBox1.Items.Add("George");
listBox1.Items.Add("Nik");
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
myItem = listBox1.SelectedItem;
listBox2.Items.Add(myItem);
}
Like that you can obtain whatever object is in listbox1 and show it in listbox 2. Just by clicking it with the mouse
Another answer is :
This is worth a read...
http://msdn.microsoft.com/en-us/library/ms171548.aspx
SendKeys.Send("r")
This might just fire once, you may want to attach a timer that starts on the MouseDown event and stops on the MouseUp event. Then you could put the SendKeys in the Timer.Tick event.
private void Form1_Load(object sender, EventArgs e)
{
this.timer1.Interval = 500;
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
timer1.Stop();
this.Text = "moose-Up";
}
private void button1_MouseDown(object sender, EventArgs e)
{
timer1.Start();
this.Text = "moose-Down";
this.richTextBox1.Select();
}
private void timer1_Tick(object sender, EventArgs e)
{
SendKeys.Send("r");
Debug.Print("tickling");
}
Select the control that you wish to receive the SendKeys value...

Categories