add ManualObservableCollection

This commit is contained in:
NaBian committed 2020-03-02 23:27:22 +08:00
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" />