mirror of
https://github.com/fadden/6502bench.git
synced 2024-11-30 01:50:10 +00:00
Fiddle with selection
There was a bigger change here, but the approach turned out to have some problems with large sets. The current app saves and restores the selected rows when you make an edit, retaining the set of selected bytes even if the number of lines changes (maybe you reformatted bytes into a string). There's no way to do that quickly with WPF when the number of selected items gets large (say 10K+). I will probably just cap the selection, and refuse to restore it if it exceeds a certain size. The ListView SelectedItems management seems to use an O(n^2) (or worse) algorithm. It might be trying to verify that items being added to SelectedItems actually exist in Items -- I can see it calling Contains(). Whatever the case, it's a big step backward performance-wise from WinForms. Yay WPF. See the DisasmUiTest project's Selection Test to see what I tried.
This commit is contained in:
parent
17af7efbbb
commit
a7d66e67e0
@ -43,13 +43,15 @@ namespace SourceGenWPF {
|
||||
///
|
||||
/// The list is initially filled with null references, with FormattedParts instances
|
||||
/// generated on demand. This is done by requesting individual items from the
|
||||
/// DisplayListGen object.
|
||||
/// LineListGen object.
|
||||
///
|
||||
/// NOTE: it may or may not be possible to implement this trivially with an
|
||||
/// ObservedCollection. At an earlier iteration it wasn't, and I'd like to keep this
|
||||
/// around even if it is now possible, in case the pendulum swings back the other way.
|
||||
/// </remarks>
|
||||
public class DisplayList : IList<DisplayList.FormattedParts>, IList,
|
||||
INotifyCollectionChanged, INotifyPropertyChanged {
|
||||
|
||||
// TODO: check VirtualizingStackPanel.VirtualizationMode == recycling (page 259)
|
||||
|
||||
/// <summary>
|
||||
/// List of formatted parts. DO NOT access this directly outside the event-sending
|
||||
/// method wrappers.
|
||||
@ -84,41 +86,6 @@ namespace SourceGenWPF {
|
||||
private const string CountString = "Count";
|
||||
private const string IndexerName = "Item[]";
|
||||
|
||||
#if false
|
||||
protected override void ClearItems() {
|
||||
base.ClearItems();
|
||||
OnPropertyChanged(CountString);
|
||||
OnPropertyChanged(IndexerName);
|
||||
OnCollectionReset();
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index) {
|
||||
FormattedParts removedItem = this[index];
|
||||
|
||||
base.RemoveItem(index);
|
||||
|
||||
OnPropertyChanged(CountString);
|
||||
OnPropertyChanged(IndexerName);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index);
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, FormattedParts item) {
|
||||
base.InsertItem(index, item);
|
||||
|
||||
OnPropertyChanged(CountString);
|
||||
OnPropertyChanged(IndexerName);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, FormattedParts item) {
|
||||
FormattedParts originalItem = this[index];
|
||||
base.SetItem(index, item);
|
||||
|
||||
OnPropertyChanged(IndexerName);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Replace, originalItem, item, index);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) {
|
||||
PropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
@ -358,9 +358,14 @@ namespace SourceGenWPF {
|
||||
|
||||
|
||||
public void OpenRecentProject(int projIndex) {
|
||||
if (DoClose()) {
|
||||
if (!DoClose()) {
|
||||
return;
|
||||
}
|
||||
//DoOpenFile(mRecentProjectPaths[projIndex]);
|
||||
if (projIndex == 0) {
|
||||
DoOpenFile(@"C:\Src\6502bench\EXTRA\ZIPPY#ff2000.dis65");
|
||||
} else {
|
||||
DoOpenFile(@"C:\Src\6502bench\EXTRA\CRYLLAN.MISSION#b30100.dis65");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,13 +45,20 @@ limitations under the License.
|
||||
<KeyGesture>Ctrl+Shift+A</KeyGesture>
|
||||
</RoutedUICommand.InputGestures>
|
||||
</RoutedUICommand>
|
||||
<RoutedUICommand x:Key="RecentProject"/>
|
||||
<RoutedUICommand x:Key="RecentProjectCmd"/>
|
||||
<RoutedUICommand x:Key="SelectAllCmd" Text="Select All">
|
||||
<RoutedUICommand.InputGestures>
|
||||
<KeyGesture>Ctrl+A</KeyGesture>
|
||||
</RoutedUICommand.InputGestures>
|
||||
</RoutedUICommand>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
|
||||
<Window.CommandBindings>
|
||||
<CommandBinding Command="{StaticResource AssembleCmd}" Executed="AssembleCmd_Executed"/>
|
||||
<CommandBinding Command="{StaticResource RecentProject}" Executed="RecentProject_Executed"/>
|
||||
<CommandBinding Command="{StaticResource RecentProjectCmd}" Executed="RecentProjectCmd_Executed"/>
|
||||
<!-- ListView has a built-in Ctrl+A handler; this only fires when codeListView not in focus -->
|
||||
<CommandBinding Command="{StaticResource SelectAllCmd}" Executed="SelectAllCmd_Executed"/>
|
||||
</Window.CommandBindings>
|
||||
|
||||
<DockPanel>
|
||||
@ -78,7 +85,7 @@ limitations under the License.
|
||||
<Separator/>
|
||||
<MenuItem Command="Copy"/>
|
||||
<Separator/>
|
||||
<MenuItem Command="SelectAll"/>
|
||||
<MenuItem Command="{StaticResource SelectAllCmd}"/>
|
||||
<MenuItem Command="Find"/>
|
||||
<MenuItem Header="Find Next"/>
|
||||
<MenuItem Header="Go To..."/>
|
||||
@ -216,11 +223,11 @@ limitations under the License.
|
||||
<Button Content="Start new project" Width="200" Height="50" Margin="10,30,10,10"/>
|
||||
<Button Content="Open existing project" Width="200" Height="50" Margin="10"/>
|
||||
<Button Content="Recent project #1" Width="200" Height="50" Margin="10"
|
||||
CommandParameter="1"
|
||||
Command="{DynamicResource RecentProject}"/>
|
||||
CommandParameter="0"
|
||||
Command="{DynamicResource RecentProjectCmd}"/>
|
||||
<Button Content="Recent project #2" Width="200" Height="50" Margin="10"
|
||||
CommandParameter="2"
|
||||
Command="{DynamicResource RecentProject}"/>
|
||||
CommandParameter="1"
|
||||
Command="{DynamicResource RecentProjectCmd}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
|
@ -18,6 +18,7 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@ -44,10 +45,15 @@ namespace SourceGenWPF.ProjWin {
|
||||
/// </summary>
|
||||
private MainController mMainCtrl;
|
||||
|
||||
private MethodInfo listViewSetSelectedItems;
|
||||
|
||||
public MainWindow() {
|
||||
InitializeComponent();
|
||||
|
||||
listViewSetSelectedItems = codeListView.GetType().GetMethod("SetSelectedItems",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
Debug.Assert(listViewSetSelectedItems != null);
|
||||
|
||||
this.DataContext = this;
|
||||
|
||||
CodeDisplayList = new DisplayList();
|
||||
@ -82,7 +88,7 @@ namespace SourceGenWPF.ProjWin {
|
||||
/// The CallerMemberName attribute puts the calling property's name in the first arg.
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of property that changed.</param>
|
||||
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") {
|
||||
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
@ -97,8 +103,8 @@ namespace SourceGenWPF.ProjWin {
|
||||
}
|
||||
set {
|
||||
mShowCodeListView = value;
|
||||
NotifyPropertyChanged("LaunchPanelVisibility");
|
||||
NotifyPropertyChanged("CodeListVisibility");
|
||||
OnPropertyChanged("LaunchPanelVisibility");
|
||||
OnPropertyChanged("CodeListVisibility");
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,19 +135,36 @@ namespace SourceGenWPF.ProjWin {
|
||||
Debug.WriteLine("assembling");
|
||||
}
|
||||
|
||||
private void RecentProject_Executed(object sender, ExecutedRoutedEventArgs e) {
|
||||
private void SelectAllCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
|
||||
DateTime start = DateTime.Now;
|
||||
|
||||
codeListView.SelectAll();
|
||||
|
||||
//codeListView.SelectedItems.Clear();
|
||||
//foreach (var item in codeListView.Items) {
|
||||
// codeListView.SelectedItems.Add(item);
|
||||
//}
|
||||
|
||||
// This seems to be faster than setting items individually (10x), but is still O(n^2)
|
||||
// or worse, and hence unsuitable for very large lists.
|
||||
//codeListView.SelectedItems.Clear();
|
||||
//listViewSetSelectedItems.Invoke(codeListView, new object[] { codeListView.Items });
|
||||
|
||||
Debug.WriteLine("Select All cmd: " + (DateTime.Now - start).Milliseconds + " ms");
|
||||
}
|
||||
|
||||
private void RecentProjectCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
|
||||
if (!int.TryParse((string)e.Parameter, out int recentIndex) ||
|
||||
recentIndex < 1 || recentIndex > MainController.MAX_RECENT_PROJECTS) {
|
||||
recentIndex < 0 || recentIndex >= MainController.MAX_RECENT_PROJECTS) {
|
||||
throw new Exception("Bad parameter: " + e.Parameter);
|
||||
}
|
||||
recentIndex--;
|
||||
|
||||
Debug.WriteLine("Recent project #" + recentIndex);
|
||||
mMainCtrl.OpenRecentProject(recentIndex);
|
||||
}
|
||||
|
||||
private void CodeListView_SelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
Debug.WriteLine("SEL: add " + e.AddedItems.Count + ", rem " + e.RemovedItems.Count);
|
||||
//Debug.WriteLine("SEL: add " + e.AddedItems.Count + ", rem " + e.RemovedItems.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +168,12 @@ namespace SourceGenWPF.Sandbox {
|
||||
}
|
||||
if (leaseObj is ILease) {
|
||||
ILease lease = (ILease)leaseObj;
|
||||
try {
|
||||
lease.Unregister(this);
|
||||
} catch (InvalidOperationException ex) {
|
||||
// TODO: not expected -- why did this start happening?
|
||||
Debug.WriteLine("WARNING: lease.Unregister threw " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
mDisposed = true;
|
||||
|
Loading…
Reference in New Issue
Block a user