Reputation: 2409
I'm not very good at the whole MVVM design pattern. Although I feel this project would benefit from it keep in mind that this project was converted from Winforms and has been I was just learning WPF at the time. That being said the idea of the project is a Keyboard wedge and Keyboard hook. When program loads the main window is made invisible and a icon on the system tray allows that main window to become visible again. When the main window is visble there is a list box in the center of it that allows for the wedges and hot keys to be assigned/defined. FYI I call the hotkeys the keys that I am monitoring from the keyboard hook. So lets say I'm monitoring HotKeys F13-F15, and I have 2 Keyboard wedges. The Item template view is simple enough.
<UserControl x:Class="Keymon.UserControls.HotkeyConfigurationView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Margin="3,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Key}" TextWrapping="Wrap" />
<TextBlock Grid.Column="1" Text="{Binding Path}" HorizontalAlignment="Right" TextWrapping="Wrap" />
</Grid>
</UserControl>
or simply put 2 text blocks side by side binding to Key and Path.
I made my Hotkeys and KeyboardWedges implement this
public interface IMyAdvancedListItem
{
string Key { get; }
string Path { get; }
string Parameters { get; }
}
When my program first starts up I check to see what computer I'm on which gives me an appropriate list of Hotkeys and Keyboard wedges. I add those to the correct lists then collect a list of both values and cast them to the IMyAdvancedListItem... which I ended up moving to the ModelView for MyAdvancedList
public class MyAdvancedListViewModel
{
public List<Keymon.ValueTypes.IMyAdvancedListItem> Keys
{
get
{
System.Collections.Generic.List<Keymon.ValueTypes.IMyAdvancedListItem> list = new System.Collections.Generic.List<ValueTypes.IMyAdvancedListItem>();
list.AddRange(Globals.MonitoredKeys.Values.Cast<Keymon.ValueTypes.IMyAdvancedListItem>());
list.AddRange(Globals.SerialPortWedges.Values.Cast<Keymon.ValueTypes.IMyAdvancedListItem>());
return list;
}
set
{
}
}
public MyAdvancedListViewModel()
{
}
}
Lastly when a user double clicks on a listbox item I have it open up the correct configuration Dialog which then updates the correct list. After it updates the list i tell it to refresh the items (doesn't appear to do anything)
private void ListBox_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var keyListBox = sender as ListBox;
var temp = keyListBox.SelectedItem as Keymon.ValueTypes.IMyAdvancedListItem;
var index = keyListBox.SelectedIndex;
if (Globals.SerialPortWedges.ContainsKey(temp.Key))
{
if (temp.Key == "MSR")
ConfigurationHelper.ConfigureWedge(temp.Key);
else
ConfigurationHelper.ConfigureWedgeEx(temp.Key);
}
else
{
ConfigurationHelper.ConfigureHotKey(temp as HotkeyConfiguration);
}
keyListBox.Items.Refresh();
keyListBox.SelectedIndex = index;
}
Now you're probably thinking well just implement INotifyCollection.. done did. I've even tried INotifyProperty. I think in the end this can all be summed up to I'm just guessing as to what I should do without understanding 100% what I'm really doing. Just for reference here is the couple of important lines of code from both of my lists.
public class GlobalHotKeys : INotifyCollectionChanged, INotifyPropertyChanged, IEnumerable
{
public GlobalHotKeys()
{
}
public HotkeyConfiguration this[int key]
{
get
{
return MonitoredKeys[key];
}
set
{
MonitoredKeys[key] = value;
NotifyCollectionChanged(NotifyCollectionChangedAction.Replace, value);
NotifyPropertyChanged("Keys");
}
}
private Dictionary<int, HotkeyConfiguration> MonitoredKeys = new Dictionary<int, HotkeyConfiguration>();
public IEnumerable<HotkeyConfiguration> Values { get { return MonitoredKeys.Values; } }
#region Notifications
private void NotifyCollectionChanged(NotifyCollectionChangedAction action, object affectedObject)
{
if (CollectionChanged != null)
{
CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, affectedObject));
}
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void NotifyPropertyChanged(string property)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
and here is the code for my wedges
public class GlobalWedges : IEnumerable, INotifyCollectionChanged
{
public GlobalWedges()
{
wedges = new Dictionary<string, KeyboardWedgeConfiguration>();
}
public KeyboardWedgeConfiguration this[string key]
{
get
{
if (wedges.ContainsKey(key))
return wedges[key];
else
return null;
}
set
{
wedges[key] = value;
NotifyCollectionChanged(NotifyCollectionChangedAction.Replace, value);
}
}
private Dictionary<string, KeyboardWedgeConfiguration> wedges;
public IEnumerable<KeyboardWedgeConfiguration> Values { get { return wedges.Values; } }
#region Notifications
private void NotifyCollectionChanged(NotifyCollectionChangedAction action, object affectedObject)
{
if (CollectionChanged != null)
{
CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, affectedObject));
}
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
}
I shouldn't think you'd need the code for the items in said lists so here is just the class outlines.
public class KeyboardWedgeConfiguration : Keymon.ValueTypes.IMyAdvancedListItem, Keymon.ValueTypes.Saveable, IDisposable
public class HotkeyConfiguration : Keymon.ValueTypes.IMyAdvancedListItem
Sorry about all the code, but better to have too much than not enough. Oh I probably forgot. I have to close my program than re-open it before I can see the changes made to either the Hot Key list, or to the Keyboard wedge. I know they are getting updated because if I update a key to say open a calculator then it does that. Any help as to why?
EDIT
This picture of the list only. After this screen shot I edited F13 to open calc.exe but the list still looks like this.
So to show the actual update I had to exit my application, then restart it. This is what I'm hoping the program will do on refresh.
Upvotes: 1
Views: 92
Reputation: 5163
The problem isn't (or wasn't since I helped you) the fact that the list isn't refreshing on the GUI, it was the fact that you were creating a copy of the ItemsSource bound to the UI, changing that copy, and doing nothing with it.
Here is where you make your copy:
var temp = keyListBox.SelectedItem as Keymon.ValueTypes.IMyAdvancedListItem;
var index = keyListBox.SelectedIndex;
Then in ConfigurationHelper.Configure____(temp.Key)
is where you'd be changing or updating this copy of the list. At this point, you were expecting the changes you made to the list to take effect in the bound ItemsSource; however since it is a copy nothing will actually happen until you update the bound list!
What you need to do is something like have your ConfigurationHelper.Configure_____(temp.Key)
return the changed item, and then take that item and update the bound ViewModel's Keys
collection and notify the GUI that the collection has changed.
Note: The problem to this question was completely resolved in private e-mail's, however this is the information I provided which got Robert started out on the right foot.
Upvotes: 1