如何在单选按钮上显示所选文件夹下的文件名
Posted
技术标签:
【中文标题】如何在单选按钮上显示所选文件夹下的文件名【英文标题】:How to show the filenames under the selected folder on the Radio Button 【发布时间】:2020-04-20 12:16:44 【问题描述】:我想在单选按钮上选择的文件夹下显示文件名。但是,我的程序仅显示 last 文件夹下的文件名。即使更改了选择,文件名也不会更改。
我尝试了一些 LINQ 语句,但没有一个有效。我怎样才能解决这个问题?这是我的完整代码:编辑:缩短代码并添加 Show_Button_Click()
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
namespace Folder_Reader
public class Question
public string QuestType get; set;
public string Label get; set;
public ObservableCollection<Answer> Answers get; set;
public class Answer : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName]string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private string _Ans;
public string Ans
get => _Ans;
set
if (_Ans == value)
return;
_Ans = value;
RaisePropertyChanged();
RaisePropertyChanged(nameof(FullName));
public string FullName => $"Ans";
public bool IsSelected get; set;
public partial class MainWindow : Window
public ObservableCollection<Question> Questions get; set;
List<string> ToBeMergedList = new List<string>();
public static readonly DependencyProperty QuestionProperty =
DependencyProperty.Register("Question", typeof(Question), typeof(MainWindow),
new FrameworkPropertyMetadata((Question)null));
public Question Question
get return (Question)GetValue(QuestionProperty);
set SetValue(QuestionProperty, value);
public MainWindow()
InitializeComponent();
RunMerge();
private void RunMerge()
string dataPath = @"C:\Data\";
DirectoryInfo[] folders = GetFoldernames(dataPath);
FillInClass();
string typePath = null;
StringBuilder sb = new StringBuilder();
foreach (var (q, toBeSelectedFolder) in from q in Questions
from toBeSelectedFolder in folders
select (q, toBeSelectedFolder))
sb.AppendLine($" toBeSelectedFolder ");
q.Answers.Add(new Answer() Ans = toBeSelectedFolder.ToString() );
// This line finally puts the LAST folder into typePath, but I don't know how to fix this
typePath = toBeSelectedFolder.FullName.ToString();
//typePath = q.Answers.Where(a => a.IsSelected).Select(a => a.Ans).ToString();
DataContext = this;
string searchPattern = "*log*.xlsx";
FileInfo[] files = GetFilenames(typePath, searchPattern);
List<Datalog> DatalogList = new List<Datalog>();
AddDatalogList(DatalogList, files);
MyListView.ItemsSource = DatalogList;
private void FillInClass()
Questions = new ObservableCollection<Question>()
new Question()
QuestType = "Radio",
Label = "Type",
Answers = new ObservableCollection<Answer>()
;
private static DirectoryInfo[] GetFoldernames(string path)
DirectoryInfo di = new DirectoryInfo(path);
DirectoryInfo[] folders =
di.GetDirectories("*", SearchOption.TopDirectoryOnly);
return folders;
private static FileInfo[] GetFilenames(string path, string searchPattern)
DirectoryInfo di = new DirectoryInfo(path);
FileInfo[] files =
di.GetFiles(searchPattern, SearchOption.AllDirectories);
return files;
private static void AddDatalogList(List<Datalog> DatalogList, FileInfo[] files)
var duplicateGroups = files.GroupBy(file => file.Name)
.Where(group => group.Count() > 1);
foreach (var group in duplicateGroups)
DatalogList.Add(new Datalog() Merge = false, Filename = group.First().FullName );
var singleGroups = files.GroupBy(file => file.Name)
.Where(group => group.Count() == 1);
foreach (var group in singleGroups)
foreach (var file in group)
DatalogList.Add(new Datalog() Merge = false, Filename = file.FullName );
private void CheckBox_Checked(object sender, RoutedEventArgs args)
ToBeMergedList.Add((sender as CheckBox).Content.ToString());
private void CheckBox_Unchecked(object sender, RoutedEventArgs args)
ToBeMergedList.Remove((sender as CheckBox).Content.ToString());
private void Show_Button_Click(object sender, RoutedEventArgs e)
foreach (Question q in Questions)
Console.WriteLine(q.Label + " : "
+ string.Join(", ", q.Answers.Where(a => a.IsSelected).Select(a => a.Ans)));
MessageBox.Show(q.Label + " : "
+ string.Join(", ", q.Answers.Where(a => a.IsSelected).Select(a => a.Ans)));
private void Merge_Button_Click(object sender, RoutedEventArgs e)
StringBuilder sb = new StringBuilder();
foreach (var toBeMergedFile in ToBeMergedList)
sb.AppendLine($" toBeMergedFile ");
MessageBox.Show(sb.ToString());
MainWindow.xaml
<Window x:Class="Folder_Reader.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:Folder_Reader"
mc:Ignorable="d"
Title="Merge Tool" Height="500" Width="450">
<Window.Resources>
<DataTemplate x:Key="RadioQuestion">
<ItemsControl ItemsSource="Binding Answers">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="Binding Ans" IsChecked="Binding IsSelected, Mode=TwoWay"
GroupName="Binding DataContext.Label,
RelativeSource=RelativeSource AncestorType=x:Type ItemsControl"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid RenderTransformOrigin="0.5,0.5">
<Grid.RowDefinitions>
<RowDefinition Height="150*"/>
<RowDefinition Height="421*"/>
</Grid.RowDefinitions>
<Border BorderThickness="2">
<ScrollViewer VerticalScrollBarVisibility="Auto" Margin="10,0,0,0">
<StackPanel>
<ItemsControl ItemsSource="Binding Questions" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Label"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Binding Label"/>
<ContentControl x:Name="ccQuestion" Grid.Column="1"
Content="Binding" Margin="10"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="Binding QuestType" Value="Radio">
<Setter TargetName="ccQuestion" Property="ContentTemplate"
Value="StaticResource RadioQuestion"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</Border>
<ListView HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Name="MyListView" Margin="0,0,0,40" Grid.Row="1">
<ListView.View>
<GridView>
<GridViewColumn Header="To-Be-Merged File">
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Margin="5,0" IsChecked="Binding Merge" Content="Binding Filename"
Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<Button Content="Show" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Margin="0,0,252.2,10.4" Width="100" Click="Show_Button_Click" Grid.Row="1" Height="21"/>
<Button Content="Merge" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Margin="0,0,73.6,10.4" Width="100" Click="Merge_Button_Click" Grid.Row="1" Height="21"/>
</Grid>
</Window>
Datalog.cs
namespace Folder_Reader
public class Datalog
public bool Merge get; set;
public string Filename get; set;
假设有三个文件夹:
文件夹 A 包含 1_log.xlsx、2_log.xlsx、3_log.xlsx FolderB 包含 4_log.xlsx、5_log.xlsx、6_log.xlsx 文件夹 C 包含 7_log.xlsx、8_log.xlsx、9_log.xlsx。在这种情况下,即使选择了 FolderA,也会显示 7_、8_、9_log.xlsx。当我在单选按钮上选择 FolderA 时,我希望看到 1_、2_、3_log.xlsx。
【问题讨论】:
好的,据我所知,您在最后一段中尝试显示文件夹和此文件夹中的所有文件。对吗?为了清楚起见,我省略了所有这些 win 表单的具体细节 @QwertyQwerty:你说得对,我试图显示所选文件夹中的所有文件。 @IanHacker:由于您只调用一次RunMerge()
,在构造函数中,您希望在选择单选按钮时如何执行它?您的代码没有多大意义。为什么不先填充要从中选择的文件夹,然后处理选择?
@mm8:(如果我错了,请阻止我,但是)我想我已经做到了。 Answer
类派生自INotifyPropertyChanged
,因此每次更改 Radio-Button 选择时,都会将更改反映到实例 Answers
。我刚刚添加了一次删除的方法Show_Button_Click()
。请选择FolderB
并点击Show
。
@IanHacker:你认为你做了什么?那么我的第一个问题呢?
【参考方案1】:
行:
FileInfo[] files = GetFilenames(typePath, searchPattern);
正在使用循环最后一次迭代中的typePath
,其中只有文件 7、8 和 9。该列表不是数据绑定的,也不会根据选择进行刷新,因此它永远不会显示任何不同。
将文件添加到答案对象(新属性),并将文件列表绑定到所选项目。这意味着为所有文件生成 Datalog 对象并将它们添加到 Answer 对象,因为该 Answer 正在添加到列表中 (q.Answers.Add(...)
)。然后,您可以将它的 ItemSource 属性绑定到选定的答案“Files”属性,而不是仅将少数文件添加到 ListView 控件(仅发生一次)。
编辑
我不得不对原始代码进行大量更改才能使其正常工作。它无论如何都不是完美的,但希望它为您指明了正确的方向。
首先,您的文件列表与 Answers 没有任何关联。我在 Answer 类中添加了一个名为 Files
的 List<Datalog>
属性来保存它们,并从 RunMerge 方法填充它。
ListView 需要绑定到任何选定的答案。这相当简单,只是对 XAML 的一个小调整:
<ListView HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Name="MyListView" Margin="0,0,0,40" Grid.Row="1"
DataContext="Binding SelectedAnswer" ItemsSource="Binding Files">
RunMerge
方法需要稍微重新排序以填充 Answers 中的 Files 属性,因为它们是构造的。我还在 Answer 中使用主视图模型类填充了TopLevel
属性,因为我们稍后需要对其进行引用。其他一些方法现在需要返回以前没有返回的地方(例如 AddDatalogList
和 FillInClass
),但这并不复杂 - 我把它留给你。
private void RunMerge()
string dataPath = @"C:\Data\";
DirectoryInfo[] folders = GetFoldernames(dataPath);
var order = FillInClass();
string typePath = null;
StringBuilder sb = new StringBuilder();
string searchPattern = "*log*.xlsx";
var viewModel = new MainWindowViewModel
Order = order
;
foreach (var (q, toBeSelectedFolder) in from q in order.Questions
from toBeSelectedFolder in folders
select (q, toBeSelectedFolder))
sb.AppendLine($" toBeSelectedFolder ");
typePath = toBeSelectedFolder.FullName.ToString();
q.Answers.Add(new Answer()
TopLevel = viewModel,
Ans = toBeSelectedFolder.ToString(),
Files = AddDatalogList(new List<Datalog>(), GetFilenames(typePath, searchPattern))
);
DataContext = viewModel;
作为 DataContext 绑定到 MainWindow 会使事情变得混乱。我将它绑定到一个MainWindowViewModel
类,该类公开两个属性:Order 和 SelectedAnswer。它看起来像这样:
public class MainWindowViewModel : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName]string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
internal void SelectedAnswerChanged()
RaisePropertyChanged(nameof(SelectedAnswer));
public Order Order get; set;
public Answer SelectedAnswer
get return Order.Questions.First().Answers.FirstOrDefault(x => x.IsSelected);
其中的SelectedAnswerChanged
方法非常重要。当用户单击单选按钮时,您在那里拥有的 TwoWay 绑定会更改底层数据模型(Answer 类)。但是,SelectedAnswer 属性不知道会触发基于该事件的 PropertyChanged 事件,因此 ListView 的 ItemsSource 不会更改。所以在Answer类的IsSelected属性的setter中,我们可以调用SelectedAnswerChanged方法从正确的地方触发PropertyChanged事件,进而触发ListView中的绑定,拾取一个新的ItemsSource……现在是在用户进行选择时更新 ListView。更新后的 Answer 类如下所示:
public class Answer : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName]string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private string _Ans;
public string Ans
get => _Ans;
set
if (_Ans == value)
return;
_Ans = value;
RaisePropertyChanged();
RaisePropertyChanged(nameof(FullName));
public string FullName => $"Ans";
private bool _isSelected;
public bool IsSelected
get
return _isSelected;
set
_isSelected = value;
RaisePropertyChanged(nameof(IsSelected));
TopLevel.SelectedAnswerChanged();
public List<Datalog> Files get; set;
public MainWindowViewModel TopLevel get; internal set;
希望您能关注这里发生的事情(并且我没有错过任何内容)。将模型与窗口分开会使阅读和修改更简单,我建议阅读 MVVM 模式。如果这是我的项目,我可以/将会做出更多更改,但我希望保持最少的工作量,以帮助说明需要添加哪些内容才能使其正常工作。
希望这会有所帮助:)
【讨论】:
谢谢,但你能发布一些代码吗?老实说,我还不能很好地理解你的概念,尽管我从昨天开始尝试过。我尝试使用注释掉的行//typePath = q.Answers.Where(a => a.IsSelected).Select(a => a.Ans).ToString();
,但它不起作用。
你的作品完美。我找到了自己的解决方案,但现在我很尴尬,所以让我停止上传。 ;-) 你介绍了MainWindowViewModel
,这让我觉得我必须更多地了解MVVM模式。非常感谢!
没问题,乐于助人:)以上是关于如何在单选按钮上显示所选文件夹下的文件名的主要内容,如果未能解决你的问题,请参考以下文章