Failing To Bind Dynamically Generated Buttons & Textboxes in WPF - c#

I was working on dynamic generation of labels, buttons and Textbox in my WPF application. Well I was successful in dynamically creating them but I am facing one major issue in it.
Xaml:
<ListBox x:Name="myViewChannelList" HorizontalAlignment="Stretch" Height="Auto" ItemsSource="{Binding}" Margin="0" VerticalAlignment="Stretch" Width="Auto">
<ListBox.ItemTemplate>
<DataTemplate >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="170" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Path=ChanelName}" Margin="50,20,0,0"></Label>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Path=VoltageText}" Height="25" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />
<Button Grid.Column="1" Content="Set" Height="25" Command="{Binding ElementName=myViewChannelList, Path=DataContext.SetCommand}" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" ></Button>
</Grid>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Model Class:
private string _ChanelName = "";
public String ChanelName
{
get
{
return _ChanelName;
}
set
{
if (value != _ChanelName)
{
_ChanelName = value;
OnPropertyChanged("ChanelName");
}
}
}
// Constructor
public VoltageModel(string ChanelName)
{
this.ChanelName = ChanelName;
}
public override string ToString()
{
return _ChanelName;
}
ViewModel Class:
class ChannelList : ObservableCollection<VoltageModel>, INotifyPropertyChanged
{
private string _VoltageText;
public string VoltageText
{
get { return _VoltageText; }
set
{
_VoltageText = value;
OnPropertyChanged("VoltageText");
}
}
// Method gets called when Set Button Is Clicked
public void SetCommandExecuted()
{
string val = VoltageText;
}
//Notify Property Changed members are present
}
Xaml.cs Class:
ChannelList myChanels = new ChannelList();
public VoltageView() // Constructor
{
InitializeComponent();
myChanels.Add(new VoltageModel("VDD__Main"));
myChanels.Add(new VoltageModel("VDD__IO__AUD"));
myChanels.Add(new VoltageModel("VDD__CODEC__AUD"));
myViewChannelList.DataContext = myChanels;
}
This gives me 3 Labels(Content as above), 3 textboxes and 3 buttons when I run the application.
Now when I enter the value inside the textbox it shows null on button click when I put a breakpoint in SetCommandExecuted(). Most importantly any of the 4 button I click generates the event. I want the first textbox and first button to be in sync(bind), 2nd textbx and 2nd button to be in sync and so on. Basically each control must be in sync with the other control in a row. It should not effect the other rows. Is it possible???

Here is the solution to your question. As general practice you want to avoid all logic, building your data, etc. in the code behind. All the business logic should be in the view model which will make it easier to unit test.
Here is the view
.xaml
<StackPanel>
<ListBox HorizontalAlignment="Stretch"
Height="Auto"
ItemsSource="{Binding VoltageCollection}"
VerticalAlignment="Stretch"
Width="Auto">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Width="100"
Content="{Binding ChannelName}" />
<TextBox Width="100"
Text="{Binding VoltageText}" />
<Button Margin="10,0,0,0"
Content="Set"
Command="{Binding VoltageCommand}"
CommandParameter="{Binding VoltageText}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
Here is the code behind
.xaml.cs
private ChannelListViewModel m_voltageViewModel;
public MainWindow()
{
InitializeComponent();
m_voltageViewModel = new ChannelListViewModel();
m_voltageViewModel.Initialize();
DataContext = m_voltageViewModel;
}
Here is the Model: VoltageModel
public class VoltageModel : INotifyPropertyChanged
{
public string ChannelName { get; set; }
private string m_voltageText;
public string VoltageText
{
get { return m_voltageText; }
set
{
m_voltageText = value;
OnPropertyChanged("VoltageText");
}
}
public ICommand VoltageCommand { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is the ViewModel: ChannelListViewModel
public class ChannelListViewModel
{
private ICommand m_voltageCommand;
public ChannelListViewModel()
{
m_voltageCommand = new DelegateCommand(x => SetCommandExecute(x));
}
public void Initialize()
{
VoltageCollection = new ObservableCollection<VoltageModel> { new VoltageModel() { ChannelName = "VDD__Main", VoltageText = String.Empty, VoltageCommand = m_voltageCommand },
new VoltageModel() { ChannelName = "VDD__IO__AUD", VoltageText = String.Empty, VoltageCommand = m_voltageCommand },
new VoltageModel() { ChannelName = "VDD__CODEC__AUD", VoltageText = String.Empty, VoltageCommand = m_voltageCommand }};
}
public ObservableCollection<VoltageModel> VoltageCollection { get; set; }
public void SetCommandExecute(object voltageText)
{
Debug.WriteLine(voltageText);
}
}
Finally simple DelegateCommand class DelegateCommand
public class DelegateCommand : ICommand
{
Action<object> m_executeDelegate;
public DelegateCommand(Action<object> executeDelegate)
{
m_executeDelegate = executeDelegate;
}
public void Execute(object parameter)
{
m_executeDelegate(parameter);
}
public bool CanExecute(object parameter) { return true; }
public event EventHandler CanExecuteChanged;
}

i didn't get much into what was wrong since i recognized 2 things that were generally very wrong ,and they might be the problem for unexpected behavior on your part .
the first : your DataTemplate places your control one on top of the other .
fix :
<DataTemplate >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Path=ChanelName}" />
<TextBox Grid.Column="1" Text="{Binding Path=VoltageText}" />
<Button Grid.Column="2" Command="{Binding ElementName=myViewChannelList, Path=DataContext.SetCommand}" />
</Grid>
</DataTemplate>
the second : your Properties are set after PropertyChanged event was risen so they would not be updated until the next time you input a value.
fix :
private T _property;
public T Property
{
get { return _property; }
set
{
_property = value;
OnPropertyChanged("Property");
}
}
make these fixes and edit your post if you still have issues post a comment under my answer.

Related

Changing Data in CollectionView (Xamarin)

I use CollectionView to show data on screen, but when I change data, UI is not changing, although I am using OnPropertyChanged. Here is the code:
Xaml
<CollectionView ItemsSource="{Binding GridData}" Margin="15">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Margin="15" Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0"
Grid.Column="0"
HorizontalTextAlignment="Start"
Text="{Binding Title}"
FontSize="Small"/>
<Label Grid.Row="0"
Grid.Column="1"
HorizontalTextAlignment="End"
Text="{Binding Data}"
TextColor="Black"
FontSize="Medium">
<Label.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={x:Reference Page} , Path=BindingContext.TapCommand}"
CommandParameter="{Binding Title}" />
</Label.GestureRecognizers>
</Label>
<BoxView Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
BackgroundColor="LightGray"
CornerRadius="2"
HorizontalOptions="FillAndExpand"
HeightRequest="1"></BoxView>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
ViewModel
private List<CollectionEntity> _gridData;
public List<CollectionEntity> GridData
{
get => _gridData;
set
{
if (_gridData != value)
{
_gridData = value;
OnPropertyChanged(nameof(GridData));
}
}
}
public ICommand TapCommand
{
get
{
return new Command<CollectionView>((commandParameters) =>
{
OpenEditing(commandParameters.ToString());
OnPropertyChanged(nameof(GridData));
});
}
}
Model (is in the same file, as is ViewModel)
public class CollectionEntity: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public string Title { get; set; }
public string Data { get; set; }
}
So, when I tap on the Label, UI does not react. I tried to write it according to this answer, but cannot understand, what is incorrect.
UPD: new command
public ICommand TapCommand => new Command<object>((commandParameters) =>
{
OpenEditing(commandParameters.ToString()); // changing data
});
Though you had write the code about INotifyPropertyChanged in your model but you didn't implement it on the property Title and Data . Modify the code like following
private string title;
public string Title
{
get => title;
set
{
if (title!= value)
{
title = value;
OnPropertyChanged(nameof(Title));
}
}
}
private string data;
public string Data
{
get => data;
set
{
if (data!= value)
{
data= value;
OnPropertyChanged(nameof(Data));
}
}
}
In addition, the code in TapCommand seems will not change the value of source . You could binding the whole model to the command and set the title or data in command as you want .
CommandParameter="{Binding .}"
public ICommand TapCommand
{
get
{
return new Command<CollectionView>((arg) =>
{
var model = arg as CollectionEntity;
// model.Title = "xxx";
});
}
}

How to Create a Method to save user input

I have the following view here where the user is able to input three different items: LockedOutBy, LockedOutFor, and LockedOutDate. I am having trouble creating a method within my view-model to allow the user to input these three items and once they hit 'ok', it will save the three items. Sorry if it is vague or if there is not enough info, please let me know if there is anything else needed.
Thank you.
View
<TextBlock VerticalAlignment="Center" Margin="5,5" Grid.Column="0" Grid.Row="1" Text="Locked Out By:"/>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding LockedOutBy, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock VerticalAlignment="Center" Margin="5,5" Grid.Column="0" Grid.Row="2" Text="Locked Out For:"/>
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding LockedOutFor, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock VerticalAlignment="Center" Margin="5,5" Grid.Column="0" Grid.Row="3" Text="Locked Out Date:"/>
<TextBox Grid.Column="1" Grid.Row="3" Text="{Binding LockedOutDate, UpdateSourceTrigger=PropertyChanged}"/>
<Button Command="{Binding Path=OKCommand}"
View-Model
This is what I have so far in my view-model.
private string _LockedOutFor;
public string LockedOutFor
{
get { return _LockedOutFor; }
set
{
_LockedOutFor = value;
OnPropertyChanged("OwnerName");
}
}
private string _LockedOutBy;
public string LockedOutBy
{
get { return _LockedOutBy; }
set
{
_LockedOutBy = value;
OnPropertyChanged("Street");
}
}
private int _LockedOutDate;
public int LockedOutDate
{
get { return _LockedOutDate; }
set
{
_LockedOutDate = value;
OnPropertyChanged("StreetOverflow");
}
}
public ICommand CancelCommand
{
get { return new RelayCommand(c => OnCancelLock()); }
}
public ICommand OKCommand
{
get { return new RelayCommand(c => OnOKLock()); }
}
protected void OnOKLock()
{
OnOK(LockedOutFor, LockedOutBy, LockedOutDate);
}
protected void OnCancelLock()
{
OnCancel();
}
ANotifyPropertyChanged - base implementation for all of our view models
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace SO60269403
{
public abstract class ANotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void SetProperty<T>(ref T backingStore, T newValue, [CallerMemberName] string propertyName = "")
{
var areEqual = ReferenceEquals(backingStore, newValue);
if (areEqual)
return;
backingStore = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
LockedOutViewModel - our view model
namespace SO60269403
{
public class LockedOutViewModel : ANotifyPropertyChanged
{
private string lockedOutBy;
private string lockedOutFor;
private int lockedOutDate;
public string LockedOutBy { get => lockedOutBy; set => SetProperty(ref lockedOutBy, value); }
public string LockedOutFor { get => lockedOutFor; set => SetProperty(ref lockedOutFor, value); }
public int LockedOutDate { get => lockedOutDate; set => SetProperty(ref lockedOutDate, value); }
}
}
ACommand - base implementation for all of our commands
using System;
using System.Windows.Input;
namespace SO60269403
{
public abstract class ACommand : ICommand
{
public event EventHandler CanExecuteChanged;
public virtual bool CanExecute(object parameter) => true;
public abstract void Execute(object parameter);
}
}
SaveCommand - our save command
namespace SO60269403
{
public class SaveCommand : ACommand
{
public LockedOutViewModel LockedOutViewModel { get; set; }
public override void Execute(object parameter)
{
//TODO
}
}
}
MainWindow - a view to data-bind our view model and hook up our save command
<Window
x:Class="SO60269403.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SO60269403">
<Window.Resources>
<local:LockedOutViewModel
x:Key="LockedOutViewModel" />
</Window.Resources>
<Grid
DataContext="{StaticResource LockedOutViewModel}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0">Locked Out By</TextBlock>
<TextBlock
Grid.Row="1"
Grid.Column="0">Locked Out For</TextBlock>
<TextBlock
Grid.Row="2"
Grid.Column="0">Locked Out Date</TextBlock>
<TextBox
Grid.Row="0"
Grid.Column="1"
Text="{Binding LockedOutBy}" />
<TextBox
Grid.Row="1"
Grid.Column="1"
Text="{Binding LockedOutFor}" />
<TextBox
Grid.Row="2"
Grid.Column="1"
Text="{Binding LockedOutDate}" />
<StackPanel
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Orientation="Horizontal"
HorizontalAlignment="Right">
<StackPanel.Resources>
<Style
TargetType="Button">
<Setter
Property="Width"
Value="100" />
</Style>
</StackPanel.Resources>
<Button>Cancel</Button>
<Button>
<Button.Command>
<local:SaveCommand
LockedOutViewModel="{StaticResource LockedOutViewModel}" />
</Button.Command>
OK
</Button>
</StackPanel>
</Grid>
</Window>
if we put a breakpoint in SaveCommand.Execute then run the app, fill in some values and click the OK button, the debugger should break on our breakpoint and we can observe that the view model's properties match what we entered. this demonstrates that the data is in place ready to be saved.

Dynamic ViewModel's bound values becomes null in WPF

I followed few online tutorials to switch between dynamic views from a ListView.
In my MainWindow, I have a ListView in the left pane with ItemsSource of list of sub ViewModels. (Each sub ViewModel implements an Interface)
Each ViewModel have its own View as a DataTemplate.
I'm calling the GenerateReport() method of the selected view from the MainWindow. But the values of selected views becomes null.
Download my source from Github.
Steps to reproduce the issue:
Run the application and type text in the Students Report's Id and Name. (The breakpoints in StudentReportViewModels's properties are properly hitting and values updated.)
Then click Generate Report button. The StudentReportViewModels's properties values becomes null.
How to fix the issue? Please help.
Source:
MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type vm:StudentsReportViewModel}">
<view:StudentsReport/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MarksReportViewModel}">
<view:MarksReport />
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding Reports}" SelectedItem="{Binding SelectedReport, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<GridSplitter Grid.Row="1" Grid.Column="1" Width="5" ResizeBehavior="PreviousAndNext" HorizontalAlignment="Stretch"/>
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ContentControl Content="{Binding SelectedReport.ViewModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</ScrollViewer>
<Button Content="Generate Report" Grid.Row="2" Margin="5" HorizontalAlignment="Right" Command="{Binding GenerateReportCommand}"/>
</Grid>
</Grid>
MainWindowViewModel:
public class MainWindowViewModel : INotifyPropertyChanged
{
private ICommand _generateReportCommand;
private Report selectedReport;
public Report SelectedReport
{
get
{
return this.selectedReport;
}
set
{
if (value != this.selectedReport)
{
this.selectedReport = value;
NotifyPropertyChanged();
}
}
}
public List<Report> Reports { get; set; }
public ICommand GenerateReportCommand
{
get
{
if (_generateReportCommand == null)
{
_generateReportCommand = new RelayCommand(
p => GenerateReport()
);
}
return _generateReportCommand;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public MainWindowViewModel()
{
Reports = new List<Report>
{
new Report{ Name = "Students Report", ViewModel = new StudentsReportViewModel()},
new Report{ Name = "Marks Report", ViewModel = new MarksReportViewModel()}
};
SelectedReport = Reports[0];
}
public void GenerateReport()
{
SelectedReport.ViewModel.GenerateReport();
}
}
StudentsReport.xaml
<TextBox Height="25" Width="100" Margin="5" Text="{Binding Id, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Height="25" Width="100" Margin="5" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
StudentsReportViewModel:
public class StudentsReportViewModel : INotifyPropertyChanged, IReportViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string id;
public string Id
{
get
{
return this.id;
}
set
{
if (value != this.id)
{
this.id = value;
NotifyPropertyChanged();
}
}
}
private string name;
public string Name
{
get
{
return this.name;
}
set
{
if (value != this.name)
{
this.name = value;
NotifyPropertyChanged();
}
}
}
public StudentsReportViewModel()
{
}
public void GenerateReport()
{
System.Windows.MessageBox.Show($"Id = {Id}, Name = {Name}");
}
}
Interface:
public interface IReportViewModel
{
void GenerateReport();
}
Model:
public class Report
{
public string Name { get; set; }
public IReportViewModel ViewModel { get; set; }
}
Your StudentsReport.xaml UserControl is binding to an instance of the StudentsReportViewModel created in the XAML:
<UserControl.DataContext>
<vm:StudentsReportViewModel/>
</UserControl.DataContext>
The Generate Report button however is calling into another instance of the StudentsReportViewModel that is create in the MainWindowVieModel constructor and is stored in the Report class.
Reports = new List<Report>
{
new Report{ Name = "Students Report", ViewModel = new StudentsReportViewModel()},
new Report{ Name = "Marks Report", ViewModel = new MarksReportViewModel()}
};
You need to remove one of these instances so that the UserControl's DataContext is bound to the same view model instance that you are generating the report message from.
I suggest deleting this code from StudentsReport.xaml:
<UserControl.DataContext>
<vm:StudentsReportViewModel/>
</UserControl.DataContext>

Define command binding in user control

I wrote user control with 2 buttons and one check box and now I want to bind Commands to data context - for each button and checkbox.
But I don't know how to define command binding. I think I'll need some kind of ICommand property in User control - but how can I connect user's data context command delegate? I want to use user control to manage each item in collection like this:
<ItemsControl ItemsSource="{Binding Path=MoneyInfo}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ChannelSetupControl
CurrentCount="{Binding Count}"
CoinValue="{Binding Value}"
UpCommand="{Binding DataContextUp}"
DownCommand="{Binding DataContextDown}"
ChangeCheckboxCommand="{Binding DataContextChange}"></local:ChannelSetupControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
XAML User control
<UserControl>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="3*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Text="{Binding CoinValue}" TextAlignment="Center"></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1" Text="{Binding CurrentCount, Mode=TwoWay}" TextAlignment="Center" VerticalAlignment="Center" FontSize="30"></TextBlock>
<StackPanel Grid.Column="1" Grid.Row="1" VerticalAlignment="Center">
<Button Content="+ 10" Padding="0 5"></Button>
<Button Content="- 10" Padding="0 5"></Button>
</StackPanel>
<CheckBox Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" IsChecked="{Binding Cycling, Mode=TwoWay}" Content="recycling" VerticalContentAlignment="Center"></CheckBox>
</Grid>
</UserControl>
and code behind and this is where I'm lost - how to define UpCommand, DownCommand and ChangeCheckboxCommand?
public partial class ChannelSetupControl : UserControl, INotifyPropertyChanged
{
private int currentCount;
private bool cycling;
private double coinValue;
public int Step { get; set; }
public double CoinValue { get { return coinValue; } set { coinValue = value; NotifyPropertyChanged("CoinValue"); } }
public int CurrentCount { get { return currentCount; } set { currentCount = value; NotifyPropertyChanged("CurrentCount"); } }
public bool Cycling { get { return cycling; } set { cycling = value; NotifyPropertyChanged("Cycling"); } }
public ChannelSetupControl()
{
InitializeComponent();
DataContext = this;
CurrentCount = 0;
Step = 10;
Cycling = false;
CoinValue = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
First of all your ChannelSetupControl class extends UserControl, so it implicitly extends DependencyObject class. It means you can use Dependency Properties instead of implementing INotifyPropertyChanged.
So you can define a dependency property in your ChannelSetupControl class, like this one:
public static readonly DependencyProperty UpCommandProperty =
DependencyProperty.Register("UpCommand", typeof(ICommand), typeof(ChannelSetupControl));
public ICommand UpCommand
{
get { return (ICommand)GetValue(UpCommandProperty); }
set { SetValue(UpCommandProperty, value); }
}
At the same time in your control XAML:
<Button Command="{Binding RelativeSource={RelativeSource Mode=Self}, Path=UpCommand, Mode=OneWay}"
Content="+ 10" Padding="0 5" />
In this way in your window XAML you can wrote:
<local:ChannelSetupControl UpCommand="{Binding UpCommand, Mode=OneWay}" ... />
You can use the same "pattern" for the other controls.
Regarding ICommand, there are a lot of implementations. The one that I prefer is the so called delegate command (for a sample you can take a look here).
I hope this quick explanation can help you.

Having problems binding my datagrid to data from my database

i've a problem to use datagrid in wpf mvvm project
Here is my xaml :
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxe="http://schemas.devexpress.com/winfx/2008/xaml/editors"
x:Class="noteManager.MainWindow"
xmlns:vm="clr-namespace:noteManager.ViewModel"
DataContext="{StaticResource noteManagerViewModel}"
Title="NoteManager" Height="490" Width="525">
<Grid Margin="0,0,0,-132.5">
<Grid.RowDefinitions>
<RowDefinition Height="10"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="200"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="110"></RowDefinition>
<RowDefinition Height="111"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"></ColumnDefinition>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="100"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
<ColumnDefinition Width="50"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Login :" FontSize="16" Grid.Column="2" Margin="51,9,50,0" Grid.RowSpan="2" Height="23" VerticalAlignment="Top" Grid.ColumnSpan="2"/>
<TextBox Text="{Binding Login}" Grid.Row="1" Grid.Column="3" Margin="14,0,86,29" Grid.ColumnSpan="2"/>
<Button Background="LightGreen" Foreground="Green" Command="{Binding testConnexion}" x:Name="testConnexion" Content="Connexion" Grid.Row="1" Grid.Column="2" Margin="51,29,86,0" Grid.ColumnSpan="3"/>
<Button Command="{Binding addUser}" Content="+" Grid.Row="1" Grid.Column="4" Margin="34,1,20,0" RenderTransformOrigin="0.742,0.468"/>
<DataGrid Name="dataGrid1" Grid.Row="2" Margin="8,7,-22,7" AutoGenerateColumns="False"
ItemsSource="{Binding _DataGridNotes}" SelectedItem="{Binding Path=MySelectedNote}" HorizontalAlignment="Center"
Width="480" Grid.ColumnSpan="6" Grid.Column="1">
<DataGrid.Columns>
<DataGridTextColumn Width="100" Binding="{Binding Path=NoteTitle}" Header="Titre" />
<DataGridTextColumn Width="200" Binding="{Binding Path=NoteContent}" Header="Note" />
<DataGridTextColumn Width="100" Binding="{Binding Path=NoteCreatedAt}" Header="Date de création" />
<DataGridTextColumn Width="100" Binding="{Binding Path=NoteUpdatedAt}" Header="Dat MAJ" />
</DataGrid.Columns>
</DataGrid>
<TextBlock Text="Titre" FontSize="16" Grid.Row="3" Grid.Column="1" Margin="27,8,7,1"/>
<TextBox Text="{Binding Path=titre, Mode=TwoWay}" Grid.Row="3" Grid.Column="2" Margin="17,10,23,10" Grid.ColumnSpan="5"/>
<TextBlock Text="Note" FontSize="16" Grid.Row="4" Grid.Column="1" Margin="27,4,7,0"/>
<TextBox Text="{Binding Path=description, Mode=TwoWay}" Grid.Row="4" Grid.Column="2" Margin="17,10,23,8" Grid.ColumnSpan="5"/>
<Button Command="{Binding Path=DeleteNote}" Background="LightPink" Foreground="red" Content="Supprimer" Grid.Row="5" Grid.Column="1" Margin="55,7,26,81" Grid.ColumnSpan="2"/>
<Button Command="{Binding Path=UpdateANote}" Content="Mettre à jour" Grid.Row="5" Grid.Column="3" Margin="14,7,67,81" Grid.ColumnSpan="2" RenderTransformOrigin="0.5,0.5"/>
<Button Command="{Binding Path=AddNote}" Content="Ajouter" Grid.Row="5" Grid.Column="4" Margin="78,7,10,81" Grid.ColumnSpan="3"/>
</Grid>
</Window>
Here is my viewModel :
namespace noteManager.ViewModel
{
public class noteManagerViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void Notify(string property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
string login;
int currentUser;
public string Login
{
get
{
return login;
}
set
{
login = value; Notify("Login");
}
}
private bool _canExecute;
public noteManagerViewModel()
{
_canExecute = true;
}
private ICommand _testConnexion;
public ICommand testConnexion
{
get
{
return _testConnexion ?? (_testConnexion = new CommandHandler(() => Connexion(), _canExecute));
}
}
private ICommand _addUser;
public ICommand addUser
{
get
{
return _addUser ?? (_addUser = new CommandHandler(() => AjoutUser(), _canExecute));
}
}
private ObservableCollection<DataGridNotes> _DataGridNotes = new ObservableCollection<DataGridNotes>();
public ObservableCollection<DataGridNotes> dataGridNotes
{
// No need for a public setter
get { return _DataGridNotes; }
}
}
the other class that i use :
public class User
{
/*public User()
{
this.Note = new HashSet<Note>();
}*/
public int Id { get; set; }
public string Login { get; set; }
//public virtual ICollection<Note> Note { get; set; }
}
public class Note : INotifyPropertyChanged
{
public int Id { get; set; }
public string NoteText { get; set; }
public string ContentText { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? UpdatedAt { get; set; }
public int UserId { get; set; }
//public virtual User User { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void Notify(string property)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public class DataGridNotes
{
private string _noteTitle;
private string _noteContent;
private string _noteCreatedAt;
private string _noteUpdatedAt;
public string NoteTitle { get { return _noteTitle; } set { _noteTitle = value; } }
public string NoteContent { get { return _noteContent; } set { _noteContent = value; } }
public string NoteCreatedAt { get { return _noteCreatedAt; } set { _noteCreatedAt = value; } }
public string NoteUpdatedAt { get { return _noteUpdatedAt; } set { _noteUpdatedAt = value; } }
}
sorry for the ugly code, new to c# for a project.
i want to use the datagrid in my viewmodel but don't find a way to make it work (would like to write data from mysql database in the datagrid
Have you an idea to make it work ?
thx in advance
Ok, It's tough to spot what you are doing wrong without seeing the ViewModel, however you may want to check the following:
1) The DataContext is correct.
2) The property _DataGridNotes exists. Check the program output to make sure that there are no warnings informing you that bindings are broken.
The property you are looking to have should look something like this:
List<Note> _DataGridNotes
{
get
{
// get notes from SQL request
// construct list of Note and return list
}
}
You should also make sure that the Note class contains the properties required (NoteTitle, NoteContent, NoteCreatedAt, NoteUpdatedAt).
It might also be worth passing back some dummy notes to debug if the problem lies in the request to the SQL database.
The problem is that you are trying to bind to a private Observable collection _DataGridNotes where you should be binding to the property dataGridNotes:
ItemsSource="{Binding dataGridNotes}"

Categories