WPF Grid Updating Issue - wpf

I am adding an item to my datasource that is bound to a datagrid, but the UI is not updating to show the new item. Here is my code.
DBContainer ctx = new DBContainer();
private void btnAddNewDesignator_Click(object sender, RoutedEventArgs e)
{
DESIGNATOR a = new DESIGNATOR();
a.DesignatorName = txtDesignator.Text;
a.TXFreq = txtTX.Text;
a.RXFreq = txtRX.Text;
ctx.AddToDESIGNATORs(a);
ctx.SaveChanges();
dgDesignators.Items.Refresh();
}
private void btnAddNewDesignator_Loaded(object sender, RoutedEventArgs e)
{
dgDesignators.ItemsSource = ctx.DESIGNATORs;
}
Here is my xaml:
<DataGrid AutoGenerateColumns="False" Height="225" HorizontalAlignment="Left" Margin="56,230,0,0" Name="dgDesignators" VerticalAlignment="Top" Width="602" CanUserAddRows="True" CanUserDeleteRows="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Designator" Binding="{Binding Path=DesignatorName,Mode=OneWay}" />
<DataGridTextColumn Header="TX Frequency" Binding="{Binding Path=TXFreq,Mode=OneWay}" />
<DataGridTextColumn Header="RX Frequency" Binding="{Binding Path=RXFreq,Mode=OneWay}" />
<DataGridTextColumn Header="In Use" />
</DataGrid.Columns>
</DataGrid>
As you can see I am refreshing, I have also tried setting the ItemSource to null first, and then rebinding it, but to no avail. Thanks for your help.

DBContainer ctx = new DBContainer();
private void btnAddNewDesignator_Click(object sender, RoutedEventArgs e)
{
DESIGNATOR a = new DESIGNATOR();
a.DesignatorName = txtDesignator.Text;
a.TXFreq = txtTX.Text;
a.RXFreq = txtRX.Text;
ctx.AddToDESIGNATORs(a);
ctx.SaveChanges();
dgDesignators.Items.Refresh();
}
private void btnAddNewDesignator_Loaded(object sender, RoutedEventArgs e)
{
dgDesignators.ItemsSource = ctx.DESIGNATORs.ToList();
}
I don't suggest this kind of code in WPF, try doing it in an MVVM pattern and it should be easier to implement and all you need to do is make a class that will act as the DataContext of the DataGrid and make that class implement INotifyPropertyChange

Button Event Handler:
private void btnAddNewDesignator_Click(object sender, RoutedEventArgs e)
{
DESIGNATOR a = new DESIGNATOR();
a.DesignatorName = txtDesignator.Text;
a.TXFreq = txtTX.Text;
a.RXFreq = txtRX.Text;
ctx.AddToDESIGNATORs(a);
ctx.SaveChanges();
designatorDataSource = ctx.DESIGNATOR.ToList();
}
ItemsSourceProperty:
private List<DESIGNATOR> _designatorDataSource = new List<DESIGNATOR>();
public List<DESIGNATOR designatorDataSource {
get {
return _designatorDataSource;
}
set {
_designatorDataSource = value;
OnPropertyChanged("designatorDataSource");
}
}
Then you bind your DataGrid:
<DataGrid Name="dgDesignators" ItemsSource={Binding designatorDataSource, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
</DataGrid>
Edit:
Adding INotifyPropertyChanged, have your class extend INotifyPropertyChanged.
Example:
using System.ComponentModel;
public class MyClass : Window, INotifyPropertyChanged
Add the Property Change Event
#region Property Change Event
/// <summary>
/// Occurs when a property is changed
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the <see cref="PropertyChanged"/> for a given
/// property.
/// </summary>
/// <param name="propertyName"></param>
protected void OnPropertyChanged(String propertyName) {
// Get the handler
PropertyChangedEventHandler handler = this.PropertyChanged;
// Check that the event handler is not null
if(null != handler) {
// Fire the event
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion

Related

DataBinding to a static singleton

I have a problem binding to a WPF form . I have my own static "settings" class (singleton) that implements PropertyChangedEventHandler and raises the event whenever a property is updated.
The singleton object is added to resources in the form's constructor and the property is correctly read on form's initialization, thus suggesting that the binding is correct.
However, WPF does NOT register any event handler for PropertyChangedEventHandler and PropertyChanged is always null. Thus the event is never raised, and my form is never updated (it's meant to be updated on a button click).
What am I doing wrong?
I suspect that calling Resources.Add for some reason prevents WPF from registering its own event handler, but I'm not sure.
I've read multiple SO questions on similar topics, but the 2 most common issues are not creating a proper singleton (thus passing another instance to xaml then intended) or not implementing INotifyPropertyChanged. I'm doing both of these correctly.
Expected behavior:
Settings.TextValue is the property I'm interested in. In its setter, NotifyPropertyChanged is called, which unfortunately fails to raise this.PropertyChanged event, since WPF registers no handler.
When MainWindow.Button1 is click, the textBox's value is supposed to change to "ButtonA OK" from the initial value of Settings.TextBox ("testOK").
Here's the code:
Settings.cs:
namespace bindings
{
public sealed class Settings : INotifyPropertyChanged
{
private static readonly Settings instance = new Settings();
private Settings()
{
}
public static Settings Instance { get { return instance; } }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName = null)
{
// passing propertyName=null raises the event for all properties
if (PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private string textValue = "testOK";
public static string TextValue
{
get { return Instance.textValue; }
set { Instance.textValue = value; Instance.NotifyPropertyChanged(); }
}
}
MainWindow.xaml.cs
namespace bindings
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
Resources.Add("foobar", Settings.Instance);
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
private void button1_Click(object sender, RoutedEventArgs e)
{
int hash = Settings.Instance.GetHashCode();
Settings.TextValue = "ButtonA OK";
}
}
}
MainWindow.xaml
<Window x:Class="bindings.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" WindowStyle="ToolWindow">
<Grid PresentationTraceSources.TraceLevel="High" DataContext="{StaticResource foobar}">
<Button Content="ButtonA" Height="33" HorizontalAlignment="Left" Margin="76,243,0,0" Name="button1" VerticalAlignment="Top" Width="101" Click="button1_Click" />
<TextBox Height="28" HorizontalAlignment="Left" Margin="182,180,0,0" Name="textBox1" VerticalAlignment="Top" Width="93"
Text="{Binding Path=TextValue, Mode=OneWay}" DataContext="{Binding}" PresentationTraceSources.TraceLevel="High"/>
</Grid>
</Window>
Thanks for help!

How to handle SelectionChanged event in via delegate command when the combobox is in a ListView cell

"WPF command support in ComboBox", this page shows how to extend a combobox to support a command, but it didn't give a dome of the delegate command that maps to the combobox's SelectedIndexChanged event. Now the problem I face is how can I handle the combobox SelectedIndexChanged event like where it is a one-off combobox situation :
<ComboBox SelectionChanged="ComboBox_SelectionChanged"></ComboBox>
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var combobox = sender as ComboBox;
if (combobox.SelectedIndex == 0)
{
//todo:
}
}
current situation is as below:
<GridViewColumn.CellTemplate>
<DataTemplate>
<Ext:CommandSupportedComboBox SelectedIndex="{Binding StartMode}"
Command="{Binding ChangeStartModeCmd}">
<ComboBoxItem>Automatically</ComboBoxItem>
<ComboBoxItem>Manual</ComboBoxItem>
<ComboBoxItem>Forbidden</ComboBoxItem>
</Ext:CommandSupportedComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
/// <summary>
/// change service start mode command
/// </summary>
public ICommand ChangeStartModeCmd { get; private set; }
and the corresponding delegate method :
/// <summary>
/// change service start mode
/// </summary>
public void ChangeStartMode()
{
//todo:
}
binding method to command:
ChangeStartModeCmd = new DelegateCommand(ChangeStartMode);
I want to define the method like this:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var combobox = sender as ComboBox;
if (combobox.SelectedIndex == 0)
{
//todo:
}
}
but how do I bind it to the delegate command ChangeStartModeCmd?
ChangeStartModeCmd = new DelegateCommand(ChangeStartMode(
/*what should I pass for the method?*/));
You probably wont need a CommandSupportedCombobox as you can attach SelectedItem property of you ComboBox and inside the setter in your ViewModel Call the funciton you want...
Xaml
<ComboBox SelectedItem="{Binding MyItem,Mode=TwoWay}" />
ViewModel
public MyItem
{
get {return myItem;}
set
{
myItem=value;
OnPropertyChanged("MyItem"); implement INotifyPropertyChanged
MyFavFunction(); // Function to be called
}
}

How ItemsControl textbox textchange event call?

in general TextBox control TextChanged event is working but in ItemsControl, the TextBox TextChanged Event is not fired how can i do this. I trying to do by using following code which I have implemented but not getting result which I want.
So, what am I doing wrong?
View
<Window x:Class="SoniSoft.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ff="clr-namespace:SoniSoft"
Title="Window1" Height="300" Width="300">
<Window.DataContext>
<ff:ViewModels/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="38"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" ff:TextBoxBehaviour.TextChangedCommand="{Binding TextChanged}" />
<ItemsControl Margin="7,0,0,0" Grid.Row="3" ItemsSource="{Binding Path=ViewModelSearchResults}" x:Name="list">
<ItemsControl.ItemTemplate>
<DataTemplate >
<Grid>
<TextBox ff:TextBoxBehaviour.TextChangedCommand="{Binding TextChanged}" Text="{Binding Path=CategoryName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="14" FontWeight="Normal" x:Name=" TextBoxCategoryName" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
View Models
class ViewModels :ViewModelBase
{
public ObservableCollection<Category> AllCategorys = new ObservableCollection<Category>();
DatabaseDataContext db = new DatabaseDataContext();
private ListCollectionView _objViewModelSearchResults;
public ListCollectionView ViewModelSearchResults
{
get { return _objViewModelSearchResults; }
set
{
_objViewModelSearchResults = value;
OnPropertyChanged("ViewModelSearchResults");
}
}
public ViewModels()
{
AllCategorys.Clear();
foreach (var item in db.Categories.OrderBy(c => c.CategoryName))
{
AllCategorys.Add(item);
}
ViewModelSearchResults = new ListCollectionView(AllCategorys);
}
public ICommand TextChanged
{
get
{
// this is very lazy: I should cache the command!
return new TextChangedCommand();
}
}
private class TextChangedCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show("Text Changed");
}
public bool CanExecute(object parameter)
{
return true;
}
}
}
DependencyProperty
class EventBehaviourFactory
{
public static DependencyProperty CreateCommandExecutionEventBehaviour(RoutedEvent routedEvent, string propertyName, Type ownerType)
{
DependencyProperty property = DependencyProperty.RegisterAttached(propertyName, typeof(ICommand), ownerType,
new PropertyMetadata(null,
new ExecuteCommandOnRoutedEventBehaviour(routedEvent).PropertyChangedHandler));
return property;
}
private class ExecuteCommandOnRoutedEventBehaviour : ExecuteCommandBehaviour
{
private readonly RoutedEvent _routedEvent;
public ExecuteCommandOnRoutedEventBehaviour(RoutedEvent routedEvent)
{
_routedEvent = routedEvent;
}
/// <summary>
/// Handles attaching or Detaching Event handlers when a Command is assigned or unassigned
/// </summary>
/// <param name="sender"></param>
/// <param name="oldValue"></param>
/// <param name="newValue"></param>
protected override void AdjustEventHandlers(DependencyObject sender, object oldValue, object newValue)
{
UIElement element = sender as UIElement;
if (element == null) { return; }
if (oldValue != null)
{
element.RemoveHandler(_routedEvent, new RoutedEventHandler(EventHandler));
}
if (newValue != null)
{
element.AddHandler(_routedEvent, new RoutedEventHandler(EventHandler));
}
}
protected void EventHandler(object sender, RoutedEventArgs e)
{
HandleEvent(sender, e);
}
}
internal abstract class ExecuteCommandBehaviour
{
protected DependencyProperty _property;
protected abstract void AdjustEventHandlers(DependencyObject sender, object oldValue, object newValue);
protected void HandleEvent(object sender, EventArgs e)
{
DependencyObject dp = sender as DependencyObject;
if (dp == null)
{
return;
}
ICommand command = dp.GetValue(_property) as ICommand;
if (command == null)
{
return;
}
if (command.CanExecute(e))
{
command.Execute(e);
}
}
/// <summary>
/// Listens for a change in the DependencyProperty that we are assigned to, and
/// adjusts the EventHandlers accordingly
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public void PropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// the first time the property changes,
// make a note of which property we are supposed
// to be watching
if (_property == null)
{
_property = e.Property;
}
object oldValue = e.OldValue;
object newValue = e.NewValue;
AdjustEventHandlers(sender, oldValue, newValue);
}
}
}
class TextBoxBehaviour
{
public static readonly DependencyProperty TextChangedCommand = EventBehaviourFactory.CreateCommandExecutionEventBehaviour(TextBox.TextChangedEvent, "TextChangedCommand", typeof(TextBoxBehaviour));
public static void SetTextChangedCommand(DependencyObject o, ICommand value)
{
o.SetValue(TextChangedCommand, value);
}
public static ICommand GetTextChangedCommand(DependencyObject o)
{
return o.GetValue(TextChangedCommand) as ICommand;
}
}
Here is the problem. You are setting the command in an ItemTemplate. Thus it is binding to the Category object you have in the ListCollectionView. Now this is the object that doesnt contain any command for your text changed. What does contain the command for your TextChanged is the DataContext of the UserControl and you need to bind it to that.
Now there are is a way to work around and its called Ancestor RelativeSource. As I work with silverlight it might work different but this line of code should do.
Edit:
The actual line should be. this because it is ofcourse a window and you need to have the DataContext (the viewmodel) and then the property TextChanged:
<TextBox ff:TextBoxBehaviour.TextChangedCommand="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}, Path=DataContext.TextChanged}" />

How do I manually refresh a databinding?

I have big query based datamodel, and I wish to display results of Linq queries into grids.
The GUI will edit attributes, which will affect the query result. However, even though the binding executes just fine, the debugger shows no subscriber to the PropertyChanged event (it is "null"). I have made this test example.
I wish for the user to set a bunch of criteria and then hit an "execute" button. In my example, I expected the number of items in the grid to change.
Here is the xaml:
<Window x:Class="GridViewNotifyTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<Button Click="Button_Click">Take 3</Button>
<Button Click="Button_Click_1">Take 5</Button>
<Button Click="Button_Click_2">FireNotify</Button>
<DataGrid ItemsSource="{Binding}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding}"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
</Window>
And here is the C#:
namespace GridViewNotifyTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window,INotifyPropertyChanged
{
private int _takeAmount;
public MainWindow()
{
InitializeComponent();
_takeAmount = 4;
DataContext = Amount;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_takeAmount = 3;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_takeAmount = 5;
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
OnPropertyValueChanged("Amount");
}
protected virtual void OnPropertyValueChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); // THE DEBUGGER SHOWS THE PROPERTYCHANGED DELEGATE AS NULL.
}
public IEnumerable<int> Amount
{
get { return Enumerable.Range(1,10).Take(_takeAmount); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
According to question title, fast answer will be to use BindingExpression.UpdateTarget method.
Set the DataContext to this and then change your Binding to be
<DataGrid ItemsSource="{Binding Amount}">

WPF ComboBox, whenever bound data changed, set SelectedIndex to 0?

I couldn't find the right event to achieve the functionality.
TargetUpdated event didn't work.
setting SelectedIndex to 0 on xaml would only affect the first load of data.
You can:
Set NotifyOnTargetUpdated on the binding
Add an event handler for Binding.TargetUpdated
In that event handler register for ItemsSource.CollectionChanged
In that event handler set the selected index to zero
The issue is most likely that you didn't set NotifyonTargetUpdated in the binding so the first event wasn't fired or that the collection was being updated but it was the same collection so the second event is necessary.
Here's a working example using a ListBox as the ItemsControl and a MessageBox as a proxy for doing whatever you want to do when the event fires.
Here is the markup:
<Grid>
<DockPanel>
<Button DockPanel.Dock="Top" Content="Update Target" Click="ButtonUpdateTarget_Click"/>
<Button DockPanel.Dock="Top" Content="Update Item" Click="ButtonUpdateItem_Click"/>
<ListBox Name="listBox" Binding.TargetUpdated="ListBox_TargetUpdated" ItemsSource="{Binding Items, NotifyOnTargetUpdated=True}"/>
</DockPanel>
</Grid>
and here is the code-behind:
public class ViewModel : INotifyPropertyChanged
{
ObservableCollection<string> items;
public ObservableCollection<string> Items
{
get { return items; }
set { items = value; OnPropertyChanged("Items"); }
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
void SetDataContext()
{
DataContext = viewModel;
viewModel.Items = new ObservableCollection<string> { "abc", "def", "ghi" };
}
ViewModel viewModel = new ViewModel();
private void ButtonUpdateTarget_Click(object sender, RoutedEventArgs e)
{
viewModel.Items = new ObservableCollection<string> { "xyz", "pdq" };
}
private void ButtonUpdateItem_Click(object sender, RoutedEventArgs e)
{
viewModel.Items[0] = "xxx";
}
private void ListBox_TargetUpdated(object sender, DataTransferEventArgs e)
{
MessageBox.Show("Target Updated!");
(listBox.ItemsSource as INotifyCollectionChanged).CollectionChanged += new NotifyCollectionChangedEventHandler(listBox_CollectionChanged);
}
void listBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
MessageBox.Show("Item Updated!");
}
Have you tried the SourceUpdated event?
I faced same problem. To overcome this problem, I used the following steps:
create a TextBox
Set visibility of TextBox to Collapsed
Bind Text to ListBox.Items.Count
<TextBox x:Name="txtCount" TextChanged="TextBox_TextChanged" Text="{Binding ElementName=ListBox1, Path=Items.Count, Mode=OneWay}" Visibility="Collapsed" />
In the TextBox_TextChanged event, set SelectedIndex to 0
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
int count = 0;
if(int.TryParse(txtCount.Text,out count) && count>0)
ListBox1.SelectedIndex = 0;
}

Resources