我不知道通知NetworkInterface 对象状态更改的一般机制。但是,如果您真的只关心从Up 到Down 的状态变化,反之亦然,您可以订阅NetworkChange.NetworkAvailabilityChanged 事件,并在引发事件时重新扫描网络接口状态。
它仍然需要检查所有接口,但至少它仅在您知道其中至少一个已更改时才会发生。这比某种轮询方法要好得多。
这是一个简短的 WPF 小程序,它利用事件来维护已知网络接口的列表,并在窗口中显示接口名称及其当前运行状态。新的适配器(即上次引发事件时不知道)以绿色背景显示,OperationalStatus 属性值发生变化的适配器以黄色背景显示,不再存在以红色背景显示(直到下次引发事件时,它们将被完全删除)。
主要工作在MainViewModel类中完成:
class MainModel : NotifyPropertyChangedBase
{
private readonly Dictionary<string, NetworkInterfaceModel> _cache = new Dictionary<string, NetworkInterfaceModel>();
private IReadOnlyList<NetworkInterfaceModel> _interfaces;
public IReadOnlyList<NetworkInterfaceModel> Interfaces
{
get { return _interfaces; }
private set { _UpdateField(ref _interfaces, value); }
}
public MainModel()
{
NetworkChange.NetworkAvailabilityChanged += _OnNetworkAvailabilityChanged;
_RebuildInterfaceList();
}
private void _OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
foreach (NetworkInterfaceModel model in _interfaces)
{
if (model.PostEventStatus == PostEventStatus.Removed)
{
_cache.Remove(model.Name);
}
else
{
// Assume removed, until proven otherwise while rebuilding list
model.SetRemoved();
}
}
_RebuildInterfaceList();
}
private void _RebuildInterfaceList()
{
foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
{
NetworkInterfaceModel model;
if (!_cache.TryGetValue(networkInterface.Name, out model))
{
model = new NetworkInterfaceModel(networkInterface);
_cache.Add(model.Name, model);
}
else
{
model.Reset();
model.OperationalStatus = networkInterface.OperationalStatus;
}
}
Interfaces = Array.AsReadOnly(_cache.Values.OrderBy(m => m.Name).ToArray());
}
}
上面包含的代码是您问题的实际答案,即订阅和处理NetworkChange.NetworkAvailabilityChanged 事件。该程序的其余部分只是为该代码提供一种操作方式并做一些有用的事情。
程序使用NetworkInterfaceModel类来跟踪每个接口的状态:
enum PostEventStatus
{
Added,
NoChange,
Changed,
Removed
}
class NetworkInterfaceModel : NotifyPropertyChangedBase
{
private string _name;
private OperationalStatus _status;
private PostEventStatus _postEventStatus = PostEventStatus.Added;
public NetworkInterfaceModel(NetworkInterface networkInterface)
{
_name = networkInterface.Name;
_status = networkInterface.OperationalStatus;
}
public string Name
{
get { return _name; }
set { _UpdateField(ref _name, value); }
}
public OperationalStatus OperationalStatus
{
get { return _status; }
set { _UpdateField(ref _status, value, _OnOperationalStatusChanged); }
}
public PostEventStatus PostEventStatus
{
get { return _postEventStatus; }
set { _UpdateField(ref _postEventStatus, value); }
}
public void Reset()
{
PostEventStatus = PostEventStatus.NoChange;
}
public void SetRemoved()
{
PostEventStatus = PostEventStatus.Removed;
}
private void _OnOperationalStatusChanged(OperationalStatus obj)
{
PostEventStatus = PostEventStatus.Changed;
}
}
这两个“模型”类都依赖于一个继承类,该类提供实现INotifyPropertyChanged 的基本功能:
class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
当然,没有 XAML 描述数据的实际表示,任何 WPF 程序都是不完整的:
<Window x:Class="TestSO43419337OperationalStatusChange.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:l="clr-namespace:TestSO43419337OperationalStatusChange"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<l:MainModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type l:NetworkInterfaceModel}">
<Border BorderBrush="Black" BorderThickness="1">
<Border.Style>
<p:Style TargetType="Border">
<p:Style.Triggers>
<DataTrigger Binding="{Binding PostEventStatus}" Value="{x:Static l:PostEventStatus.Added}">
<Setter Property="Background" Value="LightGreen"/>
</DataTrigger>
<DataTrigger Binding="{Binding PostEventStatus}" Value="{x:Static l:PostEventStatus.Changed}">
<Setter Property="Background" Value="Yellow"/>
</DataTrigger>
<DataTrigger Binding="{Binding PostEventStatus}" Value="{x:Static l:PostEventStatus.Removed}">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</p:Style.Triggers>
</p:Style>
</Border.Style>
<Grid Margin="3">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Name: " Grid.Row="0" Grid.Column="0"/>
<TextBlock Text="{Binding Name}" Grid.Row="0" Grid.Column="1"/>
<TextBlock Text="Operational Status: " Grid.Row="1" Grid.Column="0"/>
<TextBlock Text="{Binding OperationalStatus}" Grid.Row="1" Grid.Column="1"/>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Interfaces}" HorizontalContentAlignment="Stretch"/>
</Grid>
</Window>