WPF c# onStartUp() 具有多个数据输入的视图模型问题
WPF c# onStartUp() view model problem with multiple data inputs
嘿,我的 WPF 桌面应用程序 有以下 OnStartup,它转到本地目录并收集该文件夹中的所有图像(box1 是下面的示例代码):
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// set the update interval
var imageSource = new ImageSource(Path.Combine(@"C:\photos\boxes\", "box1"), TimeSpan.FromHours(1));
var viewModel = new MainWindowViewModel(imageSource);
var window = new MainWindow()
{
DataContext = viewModel
};
window.Closed += (s, a) => { viewModel.Dispose(); };
window.Show();
}
}
这对于我在 MainWindow.xaml 标签 box1 上的组件来说效果很好,但其他盒子 2- 9 不从各自的文件夹加载自己的图像 - 它们都显示与 box1 相同的图像。
目录的结构是这样的:
C:\
|-photos\
|boxes\
|box1
|box2
|box3
|box4
|box5
|box6
|box7
|box8
|box9
|box10
在 MainWindow 上,我有这段代码允许每个目录中的所有照片进入其自己的元素:
<Window.Resources>
<!-- List of supported animations -->
<FluidKit:SlideTransition x:Key="SlideTransition" x:Shared="False"/>
<FluidKit:CubeTransition x:Key="CubeTransition" Rotation="BottomToTop" x:Shared="False"/>
<FluidKit:FlipTransition x:Key="FlipTransition" x:Shared="False"/>
<local:ImageSourceConverter x:Key="ImageSourceConverter"/>
<!-- Data template for animations -->
<DataTemplate x:Key="ItemTemplate" x:Shared="False">
<Image Source="{Binding Path, Converter={StaticResource ImageSourceConverter}}"
Stretch="Fill"/>
</DataTemplate>
</Window.Resources>
<Grid>
<FluidKit:TransitionPresenter RestDuration="0:0:3"
IsLooped="True"
Transition="{StaticResource FlipTransition}"
ItemsSource="{Binding box1}"
Width="357"
Height="272"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ItemTemplate}"
x:Name="box1"
Margin="0,0,0,454"/>
<FluidKit:TransitionPresenter RestDuration="0:0:3"
IsLooped="True"
Transition="{StaticResource FlipTransition}"
ItemsSource="{Binding box2}"
Width="357"
Height="272"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ItemTemplate}"
x:Name="box2"
Margin="357,0,0,0"/>
ETC.......
就像我上面说的,第一个元素 ItemsSource="{Binding box1}"
从 box1 目录 加载图像应该是好的,但所有其他 之后的 2-9 加载了与 box1.
相同的图像
mainWindowViewModel 代码如下所示:
public class MainWindowViewModel : IDisposable
{
private readonly IDisposable _token1;
private readonly IDisposable _token2;
...ETC
public MainWindowViewModel(IImageSource imageSource)
{
// subscribe to update images at regular intervals
_token1 = imageSource
.GetImages().ObserveOn(DispatcherScheduler.Current)
.Subscribe(i => UpdateImages(i, box1), ex => ShowError(ex));
_token2 = imageSource
.GetImages().ObserveOn(DispatcherScheduler.Current)
.Subscribe(i => UpdateImages(i, box2), ex => ShowError(ex));
ETC...
}
private void ShowError(Exception ex)
{
MessageBox.Show(ex.Message, "Photo Gallery", MessageBoxButton.OK, MessageBoxImage.Error);
}
/// <summary>
/// Updates the animation's images.
/// </summary>
/// <param name="images">The images to update with</param>
/// <param name="animation">The animation to update</param>
private void UpdateImages(IEnumerable<string> images, ObservableCollection<ImageViewModel> animation)
{
animation.Clear();
foreach (var i in images)
{
animation.Add(new ImageViewModel { Path = i });
}
}
/// <summary>
/// Gets or sets a collection of images used for the first animation.
/// </summary>
public ObservableCollection<ImageViewModel> box1 { get; set; } =
new ObservableCollection<ImageViewModel>();
public ObservableCollection<ImageViewModel> box2 { get; set; } =
new ObservableCollection<ImageViewModel>();
ETC...
public void Dispose()
{
_token1.Dispose();
_token2.Dispose();
ETC...
}
}
循环获取目录中每个文件图像的函数是这样的:
public class ImageSource : IImageSource
{
private readonly string _path;
private readonly TimeSpan _interval;
public ImageSource(string path, TimeSpan interval)
{
_path = path;
_interval = interval;
}
public IObservable<IEnumerable<string>> GetImages()
{
if (!Directory.Exists(_path))
{
return Observable.Empty<IEnumerable<string>>();
}
return Observable.Create<IEnumerable<string>>(observer =>
{
return TaskPoolScheduler.Default.ScheduleAsync(async (ctrl, ct) =>
{
for (;;)
{
if (ct.IsCancellationRequested)
{
break;
}
try
{
var images = Directory.GetFiles(_path, "*.jpg");
// Don’t do anything unless there are a minimum of 10 images.
if (images.Count() > 9)
{
observer.OnNext(images.PickRandom());
}
}
catch (Exception ex)
{
observer.OnError(ex);
throw;
}
await ctrl.Sleep(_interval).ConfigureAwait(false);
}
});
});
}
代码当前转到每个 ObservableCollection<ImageViewModel> box[X] { get; set; }
并设置该文件夹中每个图像的路径。 Box2-10当然是与box 1相同的文件。
我如何修改 onStartup()
代码以允许它使用每个 box 文件夹的文件并将它们放入适当的 box # 组件而不是只使用 box1 文件?
谢谢!
更新#1
看起来 MainWindowViewModel.cs
我有 2 个错误
Error CS0535 'MainWindowViewModel' does not implement interface member 'IDisposable.Dispose()'
Error CS1503 Argument 1: cannot convert from 'System.Collections.Generic.List<PhotoGallery.ImageSource>' to 'System.Collections.Generic.IList<PhotoGallery.IImageSource>'
您需要在数据结构层次结构中多一层:BoxViewModel
public class BoxViewModel : IDisposable
{
private IDisposable _token;
public void Dispose() { /* dispose _token */}
public BoxViewModel (IImageSource imageSource)
{
// init _token, load Images
}
public ObservableCollection<ImageViewModel> Images { get; set; } =
new ObservableCollection<ImageViewModel>();
}
MainWindowViewModel 已简化:没有标记,没有图像加载,只有 BoxViewModels 的集合:
public class MainWindowViewModel : IDisposable
{
public MainWindowViewModel(Ilist<IImageSource> imageSources)
{
var boxes = imageSources.Select(x => new BoxViewModel(x));
Boxes = new ObservableCollection<BoxViewModel>(boxes);
}
public ObservableCollection<BoxViewModel> Boxes { get; set; }
}
使用列表初始化 MainWindowViewModel:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// set the update interval
var imageSources = Enumerale.Range(1,9)
.Select(n => new ImageSource(Path.Combine(@"C:\photos\boxes\", "box"+n.ToString()), TimeSpan.FromHours(1)))
.ToList();
var viewModel = new MainWindowViewModel(imageSources);
var window = new MainWindow()
{
DataContext = viewModel
};
...
}
并在视图中使用 ItemsControl:
<Grid>
<ItemsControl ItemsSource="{Binding Boxes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<FluidKit:TransitionPresenter RestDuration="0:0:3"
IsLooped="True"
Transition="{StaticResource FlipTransition}"
ItemsSource="{Binding Images}"
Width="357"
Height="272"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ItemTemplate}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
嘿,我的 WPF 桌面应用程序 有以下 OnStartup,它转到本地目录并收集该文件夹中的所有图像(box1 是下面的示例代码):
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// set the update interval
var imageSource = new ImageSource(Path.Combine(@"C:\photos\boxes\", "box1"), TimeSpan.FromHours(1));
var viewModel = new MainWindowViewModel(imageSource);
var window = new MainWindow()
{
DataContext = viewModel
};
window.Closed += (s, a) => { viewModel.Dispose(); };
window.Show();
}
}
这对于我在 MainWindow.xaml 标签 box1 上的组件来说效果很好,但其他盒子 2- 9 不从各自的文件夹加载自己的图像 - 它们都显示与 box1 相同的图像。
目录的结构是这样的:
C:\
|-photos\
|boxes\
|box1
|box2
|box3
|box4
|box5
|box6
|box7
|box8
|box9
|box10
在 MainWindow 上,我有这段代码允许每个目录中的所有照片进入其自己的元素:
<Window.Resources>
<!-- List of supported animations -->
<FluidKit:SlideTransition x:Key="SlideTransition" x:Shared="False"/>
<FluidKit:CubeTransition x:Key="CubeTransition" Rotation="BottomToTop" x:Shared="False"/>
<FluidKit:FlipTransition x:Key="FlipTransition" x:Shared="False"/>
<local:ImageSourceConverter x:Key="ImageSourceConverter"/>
<!-- Data template for animations -->
<DataTemplate x:Key="ItemTemplate" x:Shared="False">
<Image Source="{Binding Path, Converter={StaticResource ImageSourceConverter}}"
Stretch="Fill"/>
</DataTemplate>
</Window.Resources>
<Grid>
<FluidKit:TransitionPresenter RestDuration="0:0:3"
IsLooped="True"
Transition="{StaticResource FlipTransition}"
ItemsSource="{Binding box1}"
Width="357"
Height="272"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ItemTemplate}"
x:Name="box1"
Margin="0,0,0,454"/>
<FluidKit:TransitionPresenter RestDuration="0:0:3"
IsLooped="True"
Transition="{StaticResource FlipTransition}"
ItemsSource="{Binding box2}"
Width="357"
Height="272"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ItemTemplate}"
x:Name="box2"
Margin="357,0,0,0"/>
ETC.......
就像我上面说的,第一个元素 ItemsSource="{Binding box1}"
从 box1 目录 加载图像应该是好的,但所有其他 之后的 2-9 加载了与 box1.
mainWindowViewModel 代码如下所示:
public class MainWindowViewModel : IDisposable
{
private readonly IDisposable _token1;
private readonly IDisposable _token2;
...ETC
public MainWindowViewModel(IImageSource imageSource)
{
// subscribe to update images at regular intervals
_token1 = imageSource
.GetImages().ObserveOn(DispatcherScheduler.Current)
.Subscribe(i => UpdateImages(i, box1), ex => ShowError(ex));
_token2 = imageSource
.GetImages().ObserveOn(DispatcherScheduler.Current)
.Subscribe(i => UpdateImages(i, box2), ex => ShowError(ex));
ETC...
}
private void ShowError(Exception ex)
{
MessageBox.Show(ex.Message, "Photo Gallery", MessageBoxButton.OK, MessageBoxImage.Error);
}
/// <summary>
/// Updates the animation's images.
/// </summary>
/// <param name="images">The images to update with</param>
/// <param name="animation">The animation to update</param>
private void UpdateImages(IEnumerable<string> images, ObservableCollection<ImageViewModel> animation)
{
animation.Clear();
foreach (var i in images)
{
animation.Add(new ImageViewModel { Path = i });
}
}
/// <summary>
/// Gets or sets a collection of images used for the first animation.
/// </summary>
public ObservableCollection<ImageViewModel> box1 { get; set; } =
new ObservableCollection<ImageViewModel>();
public ObservableCollection<ImageViewModel> box2 { get; set; } =
new ObservableCollection<ImageViewModel>();
ETC...
public void Dispose()
{
_token1.Dispose();
_token2.Dispose();
ETC...
}
}
循环获取目录中每个文件图像的函数是这样的:
public class ImageSource : IImageSource
{
private readonly string _path;
private readonly TimeSpan _interval;
public ImageSource(string path, TimeSpan interval)
{
_path = path;
_interval = interval;
}
public IObservable<IEnumerable<string>> GetImages()
{
if (!Directory.Exists(_path))
{
return Observable.Empty<IEnumerable<string>>();
}
return Observable.Create<IEnumerable<string>>(observer =>
{
return TaskPoolScheduler.Default.ScheduleAsync(async (ctrl, ct) =>
{
for (;;)
{
if (ct.IsCancellationRequested)
{
break;
}
try
{
var images = Directory.GetFiles(_path, "*.jpg");
// Don’t do anything unless there are a minimum of 10 images.
if (images.Count() > 9)
{
observer.OnNext(images.PickRandom());
}
}
catch (Exception ex)
{
observer.OnError(ex);
throw;
}
await ctrl.Sleep(_interval).ConfigureAwait(false);
}
});
});
}
代码当前转到每个 ObservableCollection<ImageViewModel> box[X] { get; set; }
并设置该文件夹中每个图像的路径。 Box2-10当然是与box 1相同的文件。
我如何修改 onStartup()
代码以允许它使用每个 box 文件夹的文件并将它们放入适当的 box # 组件而不是只使用 box1 文件?
谢谢!
更新#1
看起来 MainWindowViewModel.cs
我有 2 个错误Error CS0535 'MainWindowViewModel' does not implement interface member 'IDisposable.Dispose()'
Error CS1503 Argument 1: cannot convert from 'System.Collections.Generic.List<PhotoGallery.ImageSource>' to 'System.Collections.Generic.IList<PhotoGallery.IImageSource>'
您需要在数据结构层次结构中多一层:BoxViewModel
public class BoxViewModel : IDisposable
{
private IDisposable _token;
public void Dispose() { /* dispose _token */}
public BoxViewModel (IImageSource imageSource)
{
// init _token, load Images
}
public ObservableCollection<ImageViewModel> Images { get; set; } =
new ObservableCollection<ImageViewModel>();
}
MainWindowViewModel 已简化:没有标记,没有图像加载,只有 BoxViewModels 的集合:
public class MainWindowViewModel : IDisposable
{
public MainWindowViewModel(Ilist<IImageSource> imageSources)
{
var boxes = imageSources.Select(x => new BoxViewModel(x));
Boxes = new ObservableCollection<BoxViewModel>(boxes);
}
public ObservableCollection<BoxViewModel> Boxes { get; set; }
}
使用列表初始化 MainWindowViewModel:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// set the update interval
var imageSources = Enumerale.Range(1,9)
.Select(n => new ImageSource(Path.Combine(@"C:\photos\boxes\", "box"+n.ToString()), TimeSpan.FromHours(1)))
.ToList();
var viewModel = new MainWindowViewModel(imageSources);
var window = new MainWindow()
{
DataContext = viewModel
};
...
}
并在视图中使用 ItemsControl:
<Grid>
<ItemsControl ItemsSource="{Binding Boxes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<FluidKit:TransitionPresenter RestDuration="0:0:3"
IsLooped="True"
Transition="{StaticResource FlipTransition}"
ItemsSource="{Binding Images}"
Width="357"
Height="272"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemTemplate="{StaticResource ItemTemplate}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>