Repository navigation
Expand file tree
/
Copy pathMainWindow.xaml
More file actions
382 lines (355 loc) · 25.2 KB
/
Copy pathMainWindow.xaml
File metadata and controls
382 lines (355 loc) · 25.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
<Window x:Class="WpfApp1.MainWindow"
xmlns="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:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="校园广播系统" Height="600" Width="1200" MinWidth="1000" MinHeight="500">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 菜单栏 -->
<Menu Grid.Row="0" DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}}">
<MenuItem Header="工具(_T)">
<MenuItem Header="批量音量…" Click="BatchVolumeButton_Click"
IsEnabled="{Binding CanUseBulkAndImportControls}"
ToolTip="将输入的音量应用到当前选中的广播项。"/>
<MenuItem Header="解析文件夹导入…" Click="ImportFolderButton_Click"
IsEnabled="{Binding CanUseBulkAndImportControls}"/>
<MenuItem Header="方案体检" Click="HealthCheckButton_Click"
IsEnabled="{Binding CanRunHealthCheck}"/>
<MenuItem Header="播放日志…" Click="LogViewerButton_Click"/>
<Separator/>
<MenuItem Header="恢复全部声音" Click="RestoreAllSoundsButton_Click"
IsEnabled="{Binding CanUseOutputSettings}"
ToolTip="解除所有输出设备上全部程序的静音。
已退出的程序其静音状态被 Windows 记住,需在该程序运行时再次点击本按钮。"/>
</MenuItem>
<MenuItem Header="帮助(_H)">
<MenuItem Header="输出模式说明…" Click="OutputModeHelpButton_Click"/>
</MenuItem>
</Menu>
<!-- 标题 -->
<StackPanel Grid.Row="1" Margin="0,0,0,10">
<TextBlock Text="校园广播系统" FontSize="24" FontWeight="Bold"/>
<Border x:Name="ConfigReadOnlyBanner" Visibility="Collapsed" Margin="0,6,0,0"
Background="#FFF3CD" BorderBrush="#E0A800" BorderThickness="1" Padding="8">
<TextBlock x:Name="ConfigReadOnlyBannerTextBlock" TextWrapping="Wrap" Foreground="#7A5C00"/>
</Border>
</StackPanel>
<!-- 主要内容区域 -->
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 左侧方案列表 -->
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 方案标题 -->
<DockPanel Grid.Row="0" LastChildFill="True" Margin="0,0,0,10">
<TextBlock Text="广播方案" FontSize="16" FontWeight="Bold" VerticalAlignment="Center"/>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" HorizontalAlignment="Right">
<Button x:Name="ManageProfilesButton" Content="管理" Width="60"
IsEnabled="{Binding CanManageProfiles, RelativeSource={RelativeSource AncestorType=Window}}"
Click="ManageProfilesButton_Click"/>
</StackPanel>
</DockPanel>
<!-- 方案列表 -->
<ListBox Grid.Row="1" Name="ProfilesListBox" Margin="0,0,10,0"
MouseDoubleClick="ProfilesListBox_MouseDoubleClick">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontWeight="{Binding IsActive, Converter={StaticResource BooleanToFontWeightConverter}}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsActive}" Value="True">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- 当前方案提示 -->
<TextBlock Grid.Row="2" Name="CurrentProfileTextBlock" Text="当前方案: 默认方案" Margin="0,5,0,0"/>
</Grid>
<!-- 中间分隔符和箭头 -->
<Grid Grid.Column="1" Width="50">
<Line X1="0" Y1="0" X2="0" Y2="1" Stroke="#CCCCCC" Stretch="Fill" StrokeThickness="1" Margin="25,10,25,10"/>
<TextBlock Text="▶" FontSize="24" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="#666666"/>
</Grid>
<!-- 右侧广播列表 -->
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 工具栏 -->
<StackPanel Grid.Row="0" Orientation="Vertical" Margin="0,0,0,10">
<!-- 第一行:高频操作 -->
<DockPanel LastChildFill="False" Margin="0,0,0,8">
<Button x:Name="AddButton" Content="添加" Width="70" Margin="0,0,6,0" DockPanel.Dock="Left"
IsEnabled="{Binding CanEditBroadcastItems, RelativeSource={RelativeSource AncestorType=Window}}"
Click="AddButton_Click"/>
<Button x:Name="EditButton" Content="编辑" Width="70" Margin="0,0,6,0" DockPanel.Dock="Left"
IsEnabled="{Binding CanEditBroadcastItems, RelativeSource={RelativeSource AncestorType=Window}}"
Click="EditButton_Click"/>
<Button x:Name="DeleteButton" Content="删除" Width="70" Margin="0,0,10,0" DockPanel.Dock="Left"
IsEnabled="{Binding CanEditBroadcastItems, RelativeSource={RelativeSource AncestorType=Window}}"
Click="DeleteButton_Click"/>
<Border Width="1" Height="24" Background="#DDDDDD" Margin="0,0,10,0" DockPanel.Dock="Left"/>
<Button x:Name="TestPlayButton" Content="测试播放" Width="80" Margin="0,0,6,0" DockPanel.Dock="Left"
IsEnabled="{Binding CanUseAuditionControls, RelativeSource={RelativeSource AncestorType=Window}}"
Click="TestPlayButton_Click"/>
<Button x:Name="SequentialTestButton" Content="连续测试" Width="80" Margin="0,0,10,0" DockPanel.Dock="Left" Click="SequentialTestButton_Click"
IsEnabled="{Binding CanUseAuditionControls, RelativeSource={RelativeSource AncestorType=Window}}"
ToolTip="从当前选中项开始,按列表顺序测试到当前方案结束。"/>
<Border Width="1" Height="24" Background="#DDDDDD" Margin="0,0,10,0" DockPanel.Dock="Left"/>
<ToggleButton x:Name="AttendantLockToggle" Content="值守锁定" Width="86" DockPanel.Dock="Right"
IsChecked="{Binding IsAttendantLocked, RelativeSource={RelativeSource AncestorType=Window}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DockPanel>
<!-- 第二行:输出设置 -->
<StackPanel Orientation="Horizontal" Margin="0,0,0,4">
<TextBlock Text="输出:" VerticalAlignment="Center" Margin="0,0,4,0"/>
<ComboBox x:Name="OutputModeComboBox" Width="100" Margin="0,0,4,0"
IsEnabled="{Binding CanUseOutputSettings, RelativeSource={RelativeSource AncestorType=Window}}"
SelectionChanged="OutputModeComboBox_SelectionChanged">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="ToolTip" Value="{Binding Description}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<Button x:Name="OutputModeHelpButton" Content="?" Width="22" Margin="0,0,10,0"
AutomationProperties.Name="输出模式说明"
ToolTip="查看三种输出模式的区别与推荐场景"
Click="OutputModeHelpButton_Click"/>
<TextBlock Text="设备:" VerticalAlignment="Center" Margin="0,0,4,0"/>
<ComboBox x:Name="AudioDeviceComboBox" Width="190" Margin="0,0,10,0"
IsEnabled="{Binding CanUseOutputSettings, RelativeSource={RelativeSource AncestorType=Window}}"
SelectionChanged="AudioDeviceComboBox_SelectionChanged">
</ComboBox>
<CheckBox x:Name="MuteOthersCheckBox" Content="独占广播" VerticalAlignment="Center"
Margin="0,0,4,0"
IsEnabled="{Binding CanUseOutputSettings, RelativeSource={RelativeSource AncestorType=Window}}"
Checked="MuteOthersCheckBox_Checked" Unchecked="MuteOthersCheckBox_Unchecked"
ToolTip="开启后:立即静音当前所有其他程序,并自动静音之后新启动的程序,每2秒兜底重扫一次。
本程序退出后其他程序仍保持静音(防止误播),需要恢复时点「恢复全部声音」。
注意:无法拦截 WASAPI 独占模式/ASIO 的专业音频软件;从未出现过的新程序首次发声瞬间可能漏出极短声音,请配合设备隔离使用。"/>
<Button x:Name="MuteOthersHelpButton" Content="?" Width="22"
AutomationProperties.Name="独占广播说明"
ToolTip="查看独占广播与输出模式的区别"
Click="OutputModeHelpButton_Click"/>
</StackPanel>
<!-- 第三行:设备隔离警告 -->
<TextBlock x:Name="DeviceIsolationHintTextBlock" HorizontalAlignment="Left"
ToolTip="物理隔离方法:在 Windows 声音设置中把「系统默认输出」设为一个不接功放的设备(如主板声卡或虚拟声卡),
并在上方「输出设备」中选择接功放的声卡。这样其他程序的声音默认流向空设备,从物理上无法进入广播功放。"/>
</StackPanel>
<!-- 广播列表 -->
<DataGrid Grid.Row="1" AutoGenerateColumns="False" Name="BroadcastDataGrid"
AlternatingRowBackground="AliceBlue" CanUserAddRows="False"
FrozenColumnCount="2" MouseDoubleClick="BroadcastDataGrid_MouseDoubleClick"
IsReadOnly="True" SelectionMode="Extended" SelectionUnit="FullRow">
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="选中项全部启用" Click="EnableAllDays_Click"/>
<MenuItem Header="选中项全部禁用" Click="DisableAllDays_Click"/>
<Separator/>
<MenuItem Header="批量勾选周几">
<MenuItem Header="周一" Tag="Monday:true" Click="BatchSetDay_Click"/>
<MenuItem Header="周二" Tag="Tuesday:true" Click="BatchSetDay_Click"/>
<MenuItem Header="周三" Tag="Wednesday:true" Click="BatchSetDay_Click"/>
<MenuItem Header="周四" Tag="Thursday:true" Click="BatchSetDay_Click"/>
<MenuItem Header="周五" Tag="Friday:true" Click="BatchSetDay_Click"/>
<MenuItem Header="周六" Tag="Saturday:true" Click="BatchSetDay_Click"/>
<MenuItem Header="周日" Tag="Sunday:true" Click="BatchSetDay_Click"/>
<Separator/>
<MenuItem Header="工作日" Tag="Weekdays:true" Click="BatchSetDay_Click"/>
<MenuItem Header="周末" Tag="Weekend:true" Click="BatchSetDay_Click"/>
</MenuItem>
<MenuItem Header="批量取消周几">
<MenuItem Header="周一" Tag="Monday:false" Click="BatchSetDay_Click"/>
<MenuItem Header="周二" Tag="Tuesday:false" Click="BatchSetDay_Click"/>
<MenuItem Header="周三" Tag="Wednesday:false" Click="BatchSetDay_Click"/>
<MenuItem Header="周四" Tag="Thursday:false" Click="BatchSetDay_Click"/>
<MenuItem Header="周五" Tag="Friday:false" Click="BatchSetDay_Click"/>
<MenuItem Header="周六" Tag="Saturday:false" Click="BatchSetDay_Click"/>
<MenuItem Header="周日" Tag="Sunday:false" Click="BatchSetDay_Click"/>
<Separator/>
<MenuItem Header="工作日" Tag="Weekdays:false" Click="BatchSetDay_Click"/>
<MenuItem Header="周末" Tag="Weekend:false" Click="BatchSetDay_Click"/>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
<DataGrid.Columns>
<!-- 时间设置(固定在最左侧) -->
<DataGridTextColumn Header="时间" Binding="{Binding TimeString}" Width="80" IsReadOnly="True"/>
<!-- 描述(固定在第二左侧) -->
<DataGridTextColumn Header="描述" Binding="{Binding Description}" Width="150" IsReadOnly="True"/>
<!-- 周选择 -->
<DataGridTemplateColumn Header="周一" Width="60" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Monday, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding CanEditGridCells, RelativeSource={RelativeSource AncestorType=Window}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="周二" Width="60" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Tuesday, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding CanEditGridCells, RelativeSource={RelativeSource AncestorType=Window}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="周三" Width="60" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Wednesday, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding CanEditGridCells, RelativeSource={RelativeSource AncestorType=Window}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="周四" Width="60" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Thursday, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding CanEditGridCells, RelativeSource={RelativeSource AncestorType=Window}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="周五" Width="60" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Friday, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding CanEditGridCells, RelativeSource={RelativeSource AncestorType=Window}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="周六" Width="60" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Saturday, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding CanEditGridCells, RelativeSource={RelativeSource AncestorType=Window}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="周日" Width="60" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Sunday, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding CanEditGridCells, RelativeSource={RelativeSource AncestorType=Window}}"
HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- 文件路径 -->
<DataGridTextColumn Header="文件路径" Binding="{Binding FilePath}" Width="200" IsReadOnly="True"/>
<!-- 音频时长 -->
<DataGridTextColumn Header="时长" Binding="{Binding Duration}" Width="80" IsReadOnly="True"/>
<!-- 循环次数 -->
<DataGridTextColumn Header="循环次数" Binding="{Binding LoopCount}" Width="80" IsReadOnly="True"/>
<!-- 音量 -->
<DataGridTemplateColumn Header="音量" Width="100" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Slider Grid.Column="0" Minimum="0" Maximum="100"
IsEnabled="{Binding CanEditGridCells, RelativeSource={RelativeSource AncestorType=Window}}"
Value="{Binding Volume, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Grid.Column="1" Text="{Binding Volume, StringFormat={}{0}%}"
Margin="5,0,0,0" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- 状态 -->
<DataGridTextColumn Header="状态" Binding="{Binding Status}" Width="80" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Grid>
<!-- 全局播放控制栏 -->
<Border Grid.Row="3" BorderBrush="#DDDDDD" BorderThickness="1" Padding="8" Margin="0,10,0,0" Background="#F8F8F8">
<Grid x:Name="PlaybackControlPanel">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="260"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="当前播放:" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock Grid.Column="1" x:Name="PlaybackItemTextBlock" Text="未播放"
MinWidth="160" MaxWidth="360" TextTrimming="CharacterEllipsis"
VerticalAlignment="Center" Margin="0,0,12,0"/>
<TextBlock Grid.Column="2" x:Name="PlaybackTimeTextBlock" Text="--:-- / --:--"
Width="150" VerticalAlignment="Center" Margin="0,0,8,0"/>
<Slider Grid.Column="3" x:Name="PlaybackProgressSlider" Minimum="0" Maximum="1" Value="0"
TickFrequency="1" IsSnapToTickEnabled="False" IsMoveToPointEnabled="True"
VerticalAlignment="Center" Margin="0,0,10,0"/>
<Button Grid.Column="4" x:Name="PauseResumeButton" Content="暂停" Width="64" Margin="0,0,6,0"
Click="PauseResumeButton_Click"/>
<Button Grid.Column="5" x:Name="StopPlaybackButton" Content="停止" Width="64" Margin="0,0,12,0"
Click="StopPlaybackButton_Click"/>
<TextBlock Grid.Column="6" Text="剩余:" VerticalAlignment="Center" Margin="0,0,4,0"/>
<TextBlock Grid.Column="7" Name="RemainingTimeTextBlock" Text="--:--" Width="72" VerticalAlignment="Center" Margin="0,0,8,0"/>
<TextBlock Grid.Column="8" VerticalAlignment="Center">
<Run Text="循环:"/>
<Run Name="RemainingLoopTextBlock" Text="--"/>
</TextBlock>
</Grid>
</Border>
<!-- 底部状态栏 -->
<StatusBar Grid.Row="4" Margin="0,8,0,0">
<StatusBarItem>
<TextBlock Text="就绪"/>
</StatusBarItem>
<Separator/>
<StatusBarItem>
<TextBlock Text="当前时间: "/>
</StatusBarItem>
<StatusBarItem>
<TextBlock Name="CurrentTimeTextBlock" Text="00:00:00"/>
</StatusBarItem>
<Separator/>
<StatusBarItem>
<TextBlock Text="下一个广播: "/>
</StatusBarItem>
<StatusBarItem>
<TextBlock Name="NextBroadcastTextBlock" Text="无"/>
</StatusBarItem>
<StatusBarItem>
<TextBlock x:Name="ToolProgressTextBlock" Text=""/>
</StatusBarItem>
</StatusBar>
</Grid>
</Window>