add ManualObservableCollection
This commit is contained in:
1 parent
98cf9ca26e
commit
08ae754ea3
5 files changed
+218
-32
No files matched your search
@@ -3,8 +3,6 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:langs="clr-namespace:HandyControlDemo.Properties.Langs"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
|
||||
xmlns:command="http://www.galasoft.ch/mvvmlight"
|
||||
xmlns:data="clr-namespace:HandyControlDemo.Data"
|
||||
DataContext="{Binding NotifyIconDemo,Source={StaticResource Locator}}"
|
||||
Background="{DynamicResource RegionBrush}">
|
||||
@@ -47,14 +45,14 @@
|
||||
<MenuItem Command="hc:ControlCommands.ShutdownApp" Header="{x:Static langs:Lang.Exit}"/>
|
||||
</ContextMenu>
|
||||
</hc:NotifyIcon.ContextMenu>
|
||||
<i:Interaction.Triggers>
|
||||
<i:EventTrigger EventName="Click">
|
||||
<command:EventToCommand Command="hc:ControlCommands.PushMainWindow2Top"/>
|
||||
</i:EventTrigger>
|
||||
<i:EventTrigger EventName="MouseDoubleClick">
|
||||
<command:EventToCommand Command="{Binding MouseCmd}" CommandParameter="MouseDoubleClick"/>
|
||||
</i:EventTrigger>
|
||||
</i:Interaction.Triggers>
|
||||
<hc:Interaction.Triggers>
|
||||
<hc:EventTrigger EventName="Click">
|
||||
<hc:EventToCommand Command="hc:ControlCommands.PushMainWindow2Top"/>
|
||||
</hc:EventTrigger>
|
||||
<hc:EventTrigger EventName="MouseDoubleClick">
|
||||
<hc:EventToCommand Command="{Binding MouseCmd}" CommandParameter="MouseDoubleClick"/>
|
||||
</hc:EventTrigger>
|
||||
</hc:Interaction.Triggers>
|
||||
</hc:NotifyIcon>
|
||||
<hc:NotifyIcon Token="{x:Static data:MessageToken.NotifyIconContextDemo}" x:Name="NotifyIconContextContent" Text="HandyControl" IsBlink="{Binding ContextContentIsBlink}" Visibility="{Binding ContextContentIsShow,Converter={StaticResource Boolean2VisibilityConverter}}" Icon="/HandyControlDemo;component/Resources/Img/icon-white.ico">
|
||||
<hc:NotifyIcon.ContextContent>
|
||||
@@ -68,14 +66,14 @@
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</hc:NotifyIcon.ContextContent>
|
||||
<i:Interaction.Triggers>
|
||||
<i:EventTrigger EventName="Click">
|
||||
<command:EventToCommand Command="hc:ControlCommands.PushMainWindow2Top"/>
|
||||
</i:EventTrigger>
|
||||
<i:EventTrigger EventName="MouseDoubleClick">
|
||||
<command:EventToCommand Command="{Binding MouseCmd}" CommandParameter="MouseDoubleClick"/>
|
||||
</i:EventTrigger>
|
||||
</i:Interaction.Triggers>
|
||||
<hc:Interaction.Triggers>
|
||||
<hc:EventTrigger EventName="Click">
|
||||
<hc:EventToCommand Command="hc:ControlCommands.PushMainWindow2Top"/>
|
||||
</hc:EventTrigger>
|
||||
<hc:EventTrigger EventName="MouseDoubleClick">
|
||||
<hc:EventToCommand Command="{Binding MouseCmd}" CommandParameter="MouseDoubleClick"/>
|
||||
</hc:EventTrigger>
|
||||
</hc:Interaction.Triggers>
|
||||
</hc:NotifyIcon>
|
||||
</hc:SimplePanel>
|
||||
</hc:TransitioningContentControl>
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:hc="https://handyorg.github.io/handycontrol"
|
||||
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
|
||||
xmlns:command="http://www.galasoft.ch/mvvmlight"
|
||||
DataContext="{Binding SideMenuDemo,Source={StaticResource Locator}}"
|
||||
Background="{DynamicResource RegionBrush}">
|
||||
|
||||
<hc:TransitioningContentControl>
|
||||
<hc:SideMenu BorderThickness="1" Width="200" Margin="32">
|
||||
<i:Interaction.Triggers>
|
||||
<i:EventTrigger EventName="SelectionChanged">
|
||||
<command:EventToCommand Command="{Binding SwitchItemCmd}" PassEventArgsToCommand="True" />
|
||||
</i:EventTrigger>
|
||||
</i:Interaction.Triggers>
|
||||
<hc:Interaction.Triggers>
|
||||
<hc:EventTrigger EventName="SelectionChanged">
|
||||
<hc:EventToCommand Command="{Binding SwitchItemCmd}" PassEventArgsToCommand="True" />
|
||||
</hc:EventTrigger>
|
||||
</hc:Interaction.Triggers>
|
||||
<hc:SideMenuItem Header="Overview">
|
||||
<hc:SideMenuItem.Icon>
|
||||
<Image Source="/HandyControlDemo;component/Resources/Img/DevOps/DevOps-Overview.png" Width="24" Height="24"/>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
<Border hc:ScrollViewer.IsInertiaEnabled="True"
|
||||
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
|
||||
xmlns:command="http://www.galasoft.ch/mvvmlight"
|
||||
x:Class="HandyControlDemo.UserControl.LeftMainContent"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Background="{DynamicResource RegionBrush}"
|
||||
@@ -61,11 +59,11 @@
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</TabControl.ContentTemplate>
|
||||
<i:Interaction.Triggers>
|
||||
<i:EventTrigger EventName="SelectionChanged">
|
||||
<command:EventToCommand Command="{Binding SwitchDemoCmd}" PassEventArgsToCommand="True" />
|
||||
</i:EventTrigger>
|
||||
</i:Interaction.Triggers>
|
||||
<hc:Interaction.Triggers>
|
||||
<hc:EventTrigger EventName="SelectionChanged">
|
||||
<hc:EventToCommand Command="{Binding SwitchDemoCmd}" PassEventArgsToCommand="True" />
|
||||
</hc:EventTrigger>
|
||||
</hc:Interaction.Triggers>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
@@ -0,0 +1,191 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace HandyControl.Collections
|
||||
{
|
||||
[Serializable]
|
||||
public class ManualObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
|
||||
{
|
||||
private SimpleMonitor _monitor = new SimpleMonitor();
|
||||
|
||||
private const string CountString = "Count";
|
||||
|
||||
private const string IndexerName = "Item[]";
|
||||
|
||||
private bool _canNotify = true;
|
||||
|
||||
public bool CanNotify
|
||||
{
|
||||
get => _canNotify;
|
||||
set
|
||||
{
|
||||
_canNotify = value;
|
||||
|
||||
if (value)
|
||||
{
|
||||
if (_oldCount != Count)
|
||||
{
|
||||
OnPropertyChanged(CountString);
|
||||
}
|
||||
|
||||
OnPropertyChanged(IndexerName);
|
||||
OnCollectionReset();
|
||||
}
|
||||
else
|
||||
{
|
||||
_oldCount = Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int _oldCount;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public ManualObservableCollection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// ReSharper disable once AssignNullToNotNullAttribute
|
||||
public ManualObservableCollection(List<T> list) : base(list != null ? new List<T>(list.Count) : list) => CopyFrom(list);
|
||||
|
||||
public ManualObservableCollection(IEnumerable<T> collection)
|
||||
{
|
||||
if (collection == null) throw new ArgumentNullException(nameof(collection));
|
||||
CopyFrom(collection);
|
||||
}
|
||||
|
||||
private void CopyFrom(IEnumerable<T> collection)
|
||||
{
|
||||
var items = Items;
|
||||
if (collection != null)
|
||||
{
|
||||
using var enumerator = collection.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
items.Add(enumerator.Current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Move(int oldIndex, int newIndex) => MoveItem(oldIndex, newIndex);
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
CheckReentrancy();
|
||||
base.ClearItems();
|
||||
OnPropertyChanged(CountString);
|
||||
OnPropertyChanged(IndexerName);
|
||||
OnCollectionReset();
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
CheckReentrancy();
|
||||
var removedItem = this[index];
|
||||
|
||||
base.RemoveItem(index);
|
||||
|
||||
OnPropertyChanged(CountString);
|
||||
OnPropertyChanged(IndexerName);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index);
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, T item)
|
||||
{
|
||||
CheckReentrancy();
|
||||
base.InsertItem(index, item);
|
||||
|
||||
OnPropertyChanged(CountString);
|
||||
OnPropertyChanged(IndexerName);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index);
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, T item)
|
||||
{
|
||||
CheckReentrancy();
|
||||
var originalItem = this[index];
|
||||
base.SetItem(index, item);
|
||||
|
||||
OnPropertyChanged(IndexerName);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Replace, originalItem, item, index);
|
||||
}
|
||||
|
||||
protected virtual void MoveItem(int oldIndex, int newIndex)
|
||||
{
|
||||
CheckReentrancy();
|
||||
|
||||
var removedItem = this[oldIndex];
|
||||
|
||||
base.RemoveItem(oldIndex);
|
||||
base.InsertItem(newIndex, removedItem);
|
||||
|
||||
OnPropertyChanged(IndexerName);
|
||||
OnCollectionChanged(NotifyCollectionChangedAction.Move, removedItem, newIndex, oldIndex);
|
||||
}
|
||||
|
||||
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
if (!CanNotify || PropertyChanged == null) return;
|
||||
|
||||
PropertyChanged.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (!CanNotify || CollectionChanged == null) return;
|
||||
|
||||
using (BlockReentrancy())
|
||||
{
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
CollectionChanged(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected IDisposable BlockReentrancy()
|
||||
{
|
||||
_monitor.Enter();
|
||||
return _monitor;
|
||||
}
|
||||
|
||||
protected void CheckReentrancy()
|
||||
{
|
||||
if (_monitor.Busy && CollectionChanged != null && CollectionChanged.GetInvocationList().Length > 1)
|
||||
{
|
||||
throw new InvalidOperationException("ObservableCollectionReentrancyNotAllowed");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(string propertyName) =>
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index) =>
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
|
||||
|
||||
private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index, int oldIndex) =>
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex));
|
||||
|
||||
private void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) =>
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index));
|
||||
|
||||
private void OnCollectionReset() =>
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
|
||||
private class SimpleMonitor : IDisposable
|
||||
{
|
||||
public void Enter() => ++_busyCount;
|
||||
|
||||
public void Dispose() => --_busyCount;
|
||||
|
||||
public bool Busy => _busyCount > 0;
|
||||
|
||||
private int _busyCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
<Import_RootNamespace>HandyControl</Import_RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Collections\ManualObservableCollection!1.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Attach\BackgroundSwitchElement.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Attach\BorderElement.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Controls\Attach\DataGridAttach.cs" />
|
||||
|
||||
Reference in new issue
Block a user