C# WPF MVVM 带参数实例化框架

问题描述 投票:0回答:1

我有一个带有 Window WINDOWCompanies 的 WPF 应用程序。在此窗口中,我有两个 Comobox 绑定到 WINDOWCompaniesVM 中的 ObservableCollections。选择行业后,公司组合框就会填充。

窗口内的框架旁边有一堆按钮。该过程应如下进行:行业选择 --> 公司选择 --> 然后按下其中一个按钮(本例中为“新闻”),并且应在当前 WINDOWCompanies 的 Frame 容器中显示具有 COMPANYNewsVM 视图模型的新 UserControl .

问题是,我不知道如何将选定的公司编号(companynum)传递给新创建的 COMPANYNewsVM。以下类用于导航。

WINDOWCompanies.xaml:

<Grid Grid.Row="1">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width=".05*"/>
        <ColumnDefinition Width=".95*"/>
    </Grid.ColumnDefinitions>

    <Grid Grid.Column="0" Background="#8a8ba2" Margin="5,0,0,5">
        <Button Style="{StaticResource MainPageButton}"
            Content="News"
            FontSize="12"
            Command="{Binding GoToFrameVM}"
            CommandParameter="8"
        />
    </Grid>

    <Frame x:Name="PageUCContent" Grid.Column="1" Margin="5,0,5,5" 
        Source="{Binding CurrentFrame}" NavigationUIVisibility="Hidden" 
            HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
            HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
        <Frame.Background>
            <SolidColorBrush Color="WhiteSmoke" Opacity="0.75"></SolidColorBrush>
        </Frame.Background>
    </Frame>
</Grid>

WINDOWCompaniesVM :

public class WINDOWCompaniesVM : _BASEVIEWMODEL
{
    private string _currentFrame;
    public string CurrentFrame
    {
        get { return _currentFrame; }
        set { SetProperty(ref _currentFrame, value, () => CurrentFrame); }
    }

    private ObservableCollection<Industry> _industryListALL;
    public ObservableCollection<Industry> IndustryListALL
    {
        get { return _industryListALL; }
        set { SetProperty(ref _industryListALL, value, () => IndustryListALL); }
    }

    private Int32 _selectedIndustryNum;
    public Int32 SelectedIndustryNum
    {
        get { return _selectedIndustryNum; }
        set
        {
            SetProperty(ref _selectedIndustryNum, value, () => SelectedIndustryNum);

            if (_selectedIndustryNum == 0)
            {
                resetCompanyNamesCB();
            }
            else if (_selectedIndustryNum != 0)
            {
                resetCompanyNamesCB();
                company = new Company();
                company.fillCompaniesNamesBYIndustryPrimaryNum(SelectedIndustryNum);
                CompanyList = company.CompaniesListALL;
                SelectedCompanyNum = 0;
            }
        }
    }

    private ObservableCollection<Company> _companyList;
    public ObservableCollection<Company> CompanyList
    {
        get { return _companyList; }
        set { SetProperty(ref _companyList, value, () => CompanyList); }
    }

    private Int32 _selectedCompanyNum;
    public Int32 SelectedCompanyNum
    {
        get { return _selectedCompanyNum; }
        set { SetProperty(ref _selectedCompanyNum, value, () => SelectedCompanyNum); }
    }

    public NavigateFrames NavigateFramesVM { get; set; }
    public CommandGoToFrame GoToFrameVM { get; set; }

    public Industry industries { get; set; }
    public Company company { get; set; }

    public WINDOWCompaniesVM() : base()
    {
        CurrentFrame = "Views/TESTUC.xaml";

        industries = new Industry();
        industries.fillIndustriesALL();
        IndustryListALL = industries.IndustriesListALL;

        NavigateFramesVM = new NavigateFrames();
        this.GoToFrameVM = new CommandGoToFrame(this);
    }

    public void GoToFrame(string parameter)
    {
        string Index = parameter;
        int ConvertedIndex = Convert.ToInt32(Index);
        NavigateFramesVM.GetFrame(ConvertedIndex);
        CurrentFrame = NavigateFramesVM.NFCurrentFrame;
    }
}

命令转到框架:

public class CommandGoToFrame : ICommand
{
    public _MAINWINDOWVM MainWindowVM { get; set; }
    public WINDOWCompaniesVM WindowCompaniesVM { get; set; }

    public CommandGoToFrame(_MAINWINDOWVM mainWindowVM)
    {
        MainWindowVM = mainWindowVM;
    }

    public CommandGoToFrame(WINDOWCompaniesVM windowCompaniesVM)
    {
        WindowCompaniesVM = windowCompaniesVM;
    }

    public event EventHandler CanExecuteChanged;
    public bool CanExecute(object parameter)
    {
        if (parameter != null)
        {
            var s = parameter as String;
            if (String.IsNullOrEmpty(s))
                return false;

            return true;
        }
        return false;
    }

    public void Execute(object parameter)
    {
        if(MainWindowVM != null)
        {
            MainWindowVM.GoToFrame(parameter as String);
        }
        else if(WindowCompaniesVM != null)
        {
            WindowCompaniesVM.GoToFrame(parameter as String);
        }
    }
}

导航框架:

public class NavigateFrames : ObservableObject
{

    private string _nfCurrentFrame;
    public string NFCurrentFrame
    {
        get { return _nfCurrentFrame; }
        set { SetProperty(ref _nfCurrentFrame, value, () => NFCurrentFrame); }
    }

    private List<string> _frames;
    public List<string> Frames
    {
        get { return _frames; }
        set { SetProperty(ref _frames, value, () => Frames); }
    }

    public NavigateFrames()
    {
        Frames = new List<string>();

        Frames.Add("Views/TESTUC.xaml");            //0
        Frames.Add("Views/ADDIndustry.xaml");       //1
        Frames.Add("Views/ADDCollege.xaml");        //2
        Frames.Add("Views/ADDCompany.xaml");        //3
        Frames.Add("Views/ADDContact.xaml");        //4
        Frames.Add("Views/ADDCompanyNews.xaml");    //5
        Frames.Add("Views/ADDJob.xaml");            //6
        Frames.Add("Views/COMPANYOverview.xaml");   //7
        Frames.Add("Views/COMPANYNews.xaml");       //8

    }

    public void GetFrame(int convertedindex)
    {
        NFCurrentFrame = Frames[convertedindex];
    }
}

公司新闻VM :

public class COMPANYNewsVM : _BASEVIEWMODEL
{
    private ObservableCollection<CompanyNews> _companyNewsList;
    public ObservableCollection<CompanyNews> CompanyNewsList
    {
        get { return _companyNewsList; }
        set { SetProperty(ref _companyNewsList, value, () => CompanyNewsList); }
    }

    private CompanyNews companynews { get; set; }

    public COMPANYNewsVM() : base()
    {
    }

    public COMPANYNewsVM(int companynum) : base()
    {
        companynews = new CompanyNews();
        companynews.fillCompanyNewsBYCompanyALL(companynum);
        CompanyNewsList = companynews.CompanyNewsALL;
    }
}

我是否错误地认为可以使用父窗口中的值在构造函数中创建一个带有参数的新用户控件?

c# wpf mvvm user-controls window
1个回答
0
投票

Frame
不应由视图模型使用视图或视图的 URI 作为导航目标来导航。视图模型绝不能实例化或处理控件,并且它不应该知道视图的 URI。

此外,您的

ICommand
实现缺少
CanExecuteChanged
事件的引发。常见模式是将事件委托给
CommandManager.RequerySuggested
事件,以使
CommandManager
为您引发此事件。

Frame
本身相当重,因为它的目的是显示HTML等高级内容。它带来了大量的开销。因此,建议使用简单且轻量级的基于
ContentControl
的普通导航(尽管
Frame
是扩展的
ContentControl
),如下所述:C# WPF 页面(视图)之间的导航

但是,您可以使用

ContentControl
Frame
属性来模仿 MVVM 导航,它是基于数据的,而不是基于视图的,因为视图模型必须保持视图不可知性。

关键是将视图模型类分配给

Frame.Content
,例如通过数据绑定,让框架基于映射到数据模型的
DataTemplate
生成视图。

以下示例是代码的重构版本,使其符合 MVVM:

class WINDOWCompaniesVM : INotifyPropertyChanegd
{
  public CommandGoToFrame GoToFrameVM => new CommandGoToFrame(this);

  private object _currentFrameData;
  public object CurrentFrameData
  {
    get => _currentFrame; 
    set => SetProperty(ref _currentFrame, value, () => CurrentFrame); 
  }

  private Int32 _selectedCompanyNum;
  public Int32 SelectedCompanyNum
  {
    get => _selectedCompanyNum; 
    set => SetProperty(ref _selectedCompanyNum, value, () => SelectedCompanyNum); 
  }

  public void GoToFrame(PageId pageId)
  {
    this.CurrentFrameData = NavigateFramesVM.GetFrame(pageId);
    if (this.CurrentFrameData is COMPANYNewsVM companyNewsVM)
    {
      companyNewsVM.Initialize(this.SelectedCompanyNum);
    }
  }
}

PageId.cs

enum PageId
{ 
  Default = 0,
  CompanyNews
}

CommandGoToFrame.cs

public class CommandGoToFrame : ICommand
{
  private readonly WINDOWCompaniesVM _windowCompaniesVM;

  public CommandGoToFrame(WINDOWCompaniesVM windowCompaniesVM)
  {
    _windowCompaniesVM = windowCompaniesVM;
  }

  // You must raise this event in order to allow the view to behave properly.
  // Either raise it explicitly or use the CommandManager.
  public event EventHandler CanExecuteChanged
  {
    add => CommandManager.RequerySuggested += value;
    remove => CommandManager.RequerySuggested -= value;
  }

  public bool CanExecute(object parameter)
    => parameter is PageId;

  public void Execute(object parameter)
    => _windowCompaniesVM.GoToFrame((PageId)parameter);
}

NavigateFrames.cs

public class NavigateFrames
{
  private object _nfCurrentFrame;
  public object NFCurrentFrame
  {
    get => _nfCurrentFrame;
    set => SetProperty(ref _nfCurrentFrame, value, () => NFCurrentFrame);
  }

  private Dictionary<PageId, Func<object>> _frames;
  public Dictionary<PageId, Func<object>> Frames
  {
    get => _frames;
    set => SetProperty(ref _frames, value, () => Frames);
  }

  public NavigateFrames()
  {
    this.Frames = new Dictionary<PageId, Func<object>>
    {
      {PageId.CompanyNews, () => new COMPANYNewsVM() }
    };
  }

  public object GetFrame(PageId pageId)
  {
    if (this.Frames.TryGetValue(pageId, out Func<object> viewModelFactory))
    {
      this.NFCurrentFrame = viewModelFactory.Invoke();
    }

    return this.NFCurrentFrame;
  }
}

公司新闻VM.cs

public class COMPANYNewsVM : _BASEVIEWMODEL
{
  private ObservableCollection<CompanyNews> _companyNewsList;
  public ObservableCollection<CompanyNews> CompanyNewsList
  {
    get => _companyNewsList;
    set => SetProperty(ref _companyNewsList, value, () => CompanyNewsList);
  }

  private readonly CompanyNews _companynews { get; set; }

  public COMPANYNewsVM() : base()
  {
    companynews = new CompanyNews();
  }

  public void Initialize(int companynum)
  {
    _companynews.fillCompanyNewsBYCompanyALL(companynum);
    this.CompanyNewsList = _companynews.CompanyNewsALL;
  }
}

MainWindow.xaml

<Window>
  <Window.DataContext>
    <COMPANYNewsVM />
  </Window.DataContext>

  <Window.Resources>

    <!-- The DataTemplate to generate the view dynamically -->
    <DataTemplate DataType="{x:Type COMPANYNewsVM}">
      <COMPANYNews />
    </DataTemplate>
  </Window.Resources>

  <StackPanel>
    <Button Content="News"           
            Command="{Binding GoToFrameVM}"
            CommandParameter="{x:Static PageId.CompanyNews}" />

    <Frame Content="{Binding CurrentFrameData}" />
  </StackPanel>
</Window>
© www.soinside.com 2019 - 2024. All rights reserved.