2013-04-10 11 views
21

मैं वर्तमान में एक सूची दृश्य (टैब के रूप में) और सामग्री संपत्ति को बाध्य करने के साथ सामग्री नियंत्रण का उपयोग करके छुपे हुए टैब के साथ एक टैब नियंत्रण की कार्यक्षमता प्राप्त करने का प्रयास कर रहा हूं।गतिशील सामग्री के लिए बाध्यकारी सामग्री नियंत्रण सामग्री

मैं उस विषय पर एक सा पढ़ सकते हैं और अगर मैं इसे का अधिकार मिल गया, यह इस तरह काम करना चाहिए:

<Grid> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="20.0*"/> 
     <ColumnDefinition Width="80.0*"/> 
    </Grid.ColumnDefinitions> 
    <ListBox Grid.Column="0"> 
     <ListBoxItem Content="Appearance"/> 
    </ListBox> 

    <ContentControl Content="{Binding SettingsPage}" Grid.Column="1"/> 
</Grid> 
. 
. 
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    <ContentControl x:Key="AppearancePage"> 
     <TextBlock Text="Test" /> 
    </ContentControl> 
    <ContentControl x:Key="AdvancedPage"> 
     <TextBlock Text="Test2" /> 
    </ContentControl> 
</ResourceDictionary> 

और पीछे कोड में: Allthough यह कोई त्रुटि फेंकता

public partial class MainWindow : MetroWindow 
    { 
    private ContentControl SettingsPage; 
    private ResourceDictionary SettingsPagesDict = new ResourceDictionary(); 

    public MainWindow() 
    { 
     InitializeComponent(); 

     SettingsPagesDict.Source = new Uri("SettingsPages.xaml", UriKind.RelativeOrAbsolute); 
     SettingsPage = SettingsPagesDict["AppearancePage"] as ContentControl; 

, यह "टेस्ट" टेक्स्टब्लॉक प्रदर्शित नहीं करता है।

इसकी संभावना है कि मुझे गलत बाध्यकारी की अवधारणा मिल गई है, कृपया मुझे सही दिशा में एक संकेत दें।

सादर

+0

और वहां सूची दृश्य कहां है? क्या आप हमें बहुत अधिक कोड जैसे बहुत अधिक कोड दे सकते हैं। हमें आपके पास सब कुछ दें। –

+0

यदि आप टैब का उपयोग करना चाहते हैं, तो आप इसके बजाय टैबकंट्रोल नियंत्रण का उपयोग क्यों नहीं करते? टैब छुपाने/दिखाने के लिए, आप TabItem नियंत्रण की दृश्यता प्रॉपर्टी में हेरफेर करते हैं (आप यहां बाध्यकारी का उपयोग कर सकते हैं)। साथ ही, माइक्रोसॉफ्ट http://msdn.microsoft.com/en-us/library/ms752347.aspx से डेटा बाइंडिंग अवलोकन का एक पठन पढ़ें। मैं आपको सलाह देता हूं कि यूआई तत्वों को बाध्य न करें। आपके उदाहरण में, मैं सेटिंगपेज के लिए एक क्लास तैयार करूंगा, जिसमें सेटिंग्स के लिए एकाधिक गुण होंगे। Xaml में, मैं नियंत्रण बनाएगा और प्रत्येक संपत्ति को बांध दूंगा। – failedprogramming

+0

@ बर्फीली गुई हेजहोग: इस तरह की सूची दृश्य महत्वपूर्ण नहीं है, यह सिर्फ बदले गए ईवेंट को ट्रिगर करने के लिए है जिसमें मैं ContentControl की सामग्री सेट करने जा रहा हूं। मूल रूप से मेरा प्रश्न यह है कि पूर्वनिर्धारित ContentControl टेम्पलेट्स का उपयोग करके कोड से सामग्री नियंत्रण की सामग्री को गतिशील रूप से कैसे बदला जाए। @ फ़ेलप्रोग्रामिंग कारण मैं ऐसा करने का प्रयास कर रहा हूं यह पोस्ट है: [link] (http://stackoverflow.com/questions/7010688/wpf-tab-control-with-no-tabs) यहां। यूआई तत्वों को बाध्य न करने की सलाह क्यों देते हैं? – Xaser

उत्तर

60

ठीक है, मैं तुम्हें दिखाने के लिए कि कैसे आप गतिशील डेटा बाइंडिंग के साथ एक MVVM (मॉडल-व्यू-ViewModel) दृष्टिकोण का उपयोग कर ContentControl की सामग्री को बदल सकते हैं एक सरल उदाहरण खटखटाया है।

मैं अनुशंसा करता हूं कि आप एक नई परियोजना बनाएं और यह देखने के लिए इन फ़ाइलों को लोड करें कि यह सब कैसे काम करता है।

हमें पहले INotifyPropertyChanged इंटरफ़ेस को लागू करने की आवश्यकता है। यह आपको अपनी खुद की कक्षाओं को गुणों के साथ परिभाषित करने की अनुमति देगा जो यूआई को सूचित करेंगे जब गुणों में परिवर्तन होता है। हम एक अमूर्त वर्ग बनाते हैं जो इस कार्यक्षमता को प्रदान करता है।

ViewModelBase.cs

public abstract class ViewModelBase : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged(string propertyName) 
    { 
     this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); 
    } 

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) 
    { 
     var handler = this.PropertyChanged; 
     if (handler != null) 
     { 
      handler(this, e); 
     } 
    } 
} 

अब हम डेटा मॉडल की आवश्यकता है। सादगी के लिए, मैंने 2 मॉडल बनाए हैं - होमपेज और सेटिंग्सपेज। दोनों मॉडलों में केवल एक ही संपत्ति होती है, आप आवश्यकतानुसार अधिक गुण जोड़ सकते हैं।

HomePage.cs

public class HomePage 
{ 
    public string PageTitle { get; set; } 
} 

SettingsPage.cs

public class SettingsPage 
{ 
    public string PageTitle { get; set; } 
} 

मैं तो इसी ViewModels एक मॉडल रैप करने के लिए पैदा करते हैं। ध्यान दें कि व्यूमोडल्स को मेरे व्यूमोडेलबेस सार तत्व से प्राप्त होता है।

HomePageViewModel.cs

public class HomePageViewModel : ViewModelBase 
{ 
    public HomePageViewModel(HomePage model) 
    { 
     this.Model = model; 
    } 

    public HomePage Model { get; private set; } 

    public string PageTitle 
    { 
     get 
     { 
      return this.Model.PageTitle; 
     } 
     set 
     { 
      this.Model.PageTitle = value; 
      this.OnPropertyChanged("PageTitle"); 
     } 
    } 
} 

SettingsPageViewModel.cs

public class SettingsPageViewModel : ViewModelBase 
{ 
    public SettingsPageViewModel(SettingsPage model) 
    { 
     this.Model = model; 
    } 

    public SettingsPage Model { get; private set; } 

    public string PageTitle 
    { 
     get 
     { 
      return this.Model.PageTitle; 
     } 
     set 
     { 
      this.Model.PageTitle = value; 
      this.OnPropertyChanged("PageTitle"); 
     } 
    } 
} 

अब हम प्रत्येक ViewModel के लिए दृश्य प्रदान करने के लिए की जरूरत है। यानी होमपेज व्यू और सेटिंग्सपेज व्यू। मैंने इसके लिए 2 उपयोगकर्ता नियंत्रण बनाए।

होमपेज व्यू।XAML

<UserControl x:Class="WpfApplication3.HomePageView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300"> 
<Grid> 
     <TextBlock FontSize="20" Text="{Binding Path=PageTitle}" /> 
</Grid> 

SettingsPageView.xaml

<UserControl x:Class="WpfApplication3.SettingsPageView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300"> 
<Grid> 
    <TextBlock FontSize="20" Text="{Binding Path=PageTitle}" /> 
</Grid> 

अब हम MainWindow के लिए XAML को परिभाषित करने की जरूरत है। मैंने 2 "पृष्ठों" के बीच नेविगेट करने में सहायता के लिए 2 बटन शामिल किए हैं। MainWindow.xaml

<Window x:Class="WpfApplication3.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:WpfApplication3" 
    Title="MainWindow" Height="350" Width="525"> 
<Window.Resources> 
    <DataTemplate DataType="{x:Type local:HomePageViewModel}"> 
     <local:HomePageView /> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type local:SettingsPageViewModel}"> 
     <local:SettingsPageView /> 
    </DataTemplate> 
</Window.Resources> 
<DockPanel> 
    <StackPanel DockPanel.Dock="Left"> 
     <Button Content="Home Page" Command="{Binding Path=LoadHomePageCommand}" /> 
     <Button Content="Settings Page" Command="{Binding Path=LoadSettingsPageCommand}"/> 
    </StackPanel> 

    <ContentControl Content="{Binding Path=CurrentViewModel}"></ContentControl> 
</DockPanel> 

हम भी MainWindow के लिए एक ViewModel की जरूरत है। लेकिन इससे पहले हमें एक और कक्षा बनाने की जरूरत है ताकि हम अपने बटन को कमांड से बांध सकें।

DelegateCommand.cs

public class DelegateCommand : ICommand 
{ 
    /// <summary> 
    /// Action to be performed when this command is executed 
    /// </summary> 
    private Action<object> executionAction; 

    /// <summary> 
    /// Predicate to determine if the command is valid for execution 
    /// </summary> 
    private Predicate<object> canExecutePredicate; 

    /// <summary> 
    /// Initializes a new instance of the DelegateCommand class. 
    /// The command will always be valid for execution. 
    /// </summary> 
    /// <param name="execute">The delegate to call on execution</param> 
    public DelegateCommand(Action<object> execute) 
     : this(execute, null) 
    { 
    } 

    /// <summary> 
    /// Initializes a new instance of the DelegateCommand class. 
    /// </summary> 
    /// <param name="execute">The delegate to call on execution</param> 
    /// <param name="canExecute">The predicate to determine if command is valid for execution</param> 
    public DelegateCommand(Action<object> execute, Predicate<object> canExecute) 
    { 
     if (execute == null) 
     { 
      throw new ArgumentNullException("execute"); 
     } 

     this.executionAction = execute; 
     this.canExecutePredicate = canExecute; 
    } 

    /// <summary> 
    /// Raised when CanExecute is changed 
    /// </summary> 
    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 

    /// <summary> 
    /// Executes the delegate backing this DelegateCommand 
    /// </summary> 
    /// <param name="parameter">parameter to pass to predicate</param> 
    /// <returns>True if command is valid for execution</returns> 
    public bool CanExecute(object parameter) 
    { 
     return this.canExecutePredicate == null ? true : this.canExecutePredicate(parameter); 
    } 

    /// <summary> 
    /// Executes the delegate backing this DelegateCommand 
    /// </summary> 
    /// <param name="parameter">parameter to pass to delegate</param> 
    /// <exception cref="InvalidOperationException">Thrown if CanExecute returns false</exception> 
    public void Execute(object parameter) 
    { 
     if (!this.CanExecute(parameter)) 
     { 
      throw new InvalidOperationException("The command is not valid for execution, check the CanExecute method before attempting to execute."); 
     } 
     this.executionAction(parameter); 
    } 
} 

और अब हम MainWindowViewModel defind कर सकते हैं। CurrentViewModel वह संपत्ति है जो MainWindow पर ContentControl से जुड़ी है। जब हम बटन पर क्लिक करके इस संपत्ति को बदलते हैं, तो स्क्रीन मेनविंडो पर बदल जाती है। मेनविंडो जानता है कि विंडो में परिभाषित डेटा टेम्पलेट्स के कारण कौन सी स्क्रीन (usercontrol) लोड होनी चाहिए। स्रोत खंड।

MainWindowViewModel.cs

public class MainWindowViewModel : ViewModelBase 
{ 
    public MainWindowViewModel() 
    { 
     this.LoadHomePage(); 

     // Hook up Commands to associated methods 
     this.LoadHomePageCommand = new DelegateCommand(o => this.LoadHomePage()); 
     this.LoadSettingsPageCommand = new DelegateCommand(o => this.LoadSettingsPage()); 
    } 

    public ICommand LoadHomePageCommand { get; private set; } 
    public ICommand LoadSettingsPageCommand { get; private set; } 

    // ViewModel that is currently bound to the ContentControl 
    private ViewModelBase _currentViewModel; 

    public ViewModelBase CurrentViewModel 
    { 
     get { return _currentViewModel; } 
     set 
     { 
      _currentViewModel = value; 
      this.OnPropertyChanged("CurrentViewModel"); 
     } 
    } 

    private void LoadHomePage() 
    { 
     CurrentViewModel = new HomePageViewModel(
      new HomePage() { PageTitle = "This is the Home Page."}); 
    } 

    private void LoadSettingsPage() 
    { 
     CurrentViewModel = new SettingsPageViewModel(
      new SettingsPage(){PageTitle = "This is the Settings Page."}); 
    } 
} 

और अंत में, हम इतना है कि हम MainWindow की DataContext संपत्ति में हमारे MainWindowViewModel वर्ग लोड कर सकते हैं आवेदन स्टार्टअप ओवरराइड करने के लिए की जरूरत है।

App.xaml.cs

public partial class App : Application 
{ 
    protected override void OnStartup(StartupEventArgs e) 
    { 
     base.OnStartup(e); 

     var window = new MainWindow() { DataContext = new MainWindowViewModel() }; 
     window.Show(); 
    } 
} 

यह भी ताकि हम शुरू अप पर 2 MainWindows नहीं मिलता App.xaml आवेदन टैग में StartupUri="MainWindow.xaml" कोड निकालने के लिए एक अच्छा विचार होगा।

ध्यान दें कि DelegateCommand और ViewModelBase कक्षाएं जिन्हें केवल नई परियोजनाओं में कॉपी किया जा सकता है और उपयोग किया जा सकता है। यह सिर्फ एक बहुत ही सरल उदाहरण है। आप here और here

संपादित अपनी टिप्पणी में से एक बेहतर विचार प्राप्त कर सकते हैं, आप को पता है अगर यह प्रत्येक दृश्य और संबंधित बॉयलरप्लेट कोड के लिए एक वर्ग के लिए नहीं किया जा सकता है चाहता था। जहां तक ​​मुझे पता है, जवाब नहीं है। हां, आपके पास एक विशाल श्रेणी हो सकती है, लेकिन आपको अभी भी प्रत्येक प्रॉपर्टी सेटर के लिए ऑनप्रॉपर्टी चेंज करने की आवश्यकता होगी। इसमें कुछ कमियां भी हैं। सबसे पहले, परिणामी वर्ग को बनाए रखना वाकई मुश्किल होगा। बहुत सारे कोड और निर्भरताएं होंगी। दूसरा, डेटा टेम्पलेट्स को "स्वैप" दृश्यों में उपयोग करना मुश्किल होगा। यह आपके डेटा टेम्पलेट्स में एक्स: कुंजी का उपयोग करके और आपके उपयोगकर्ता नियंत्रण में एक टेम्पलेट बाध्यकारी हार्डकोडिंग के द्वारा अभी भी संभव है। संक्षेप में, आप वास्तव में अपना कोड बहुत छोटा नहीं बना रहे हैं, लेकिन आप इसे अपने लिए कठिन बना देंगे।

मुझे लगता है कि आपकी मुख्य पकड़ को आपके मॉडल गुणों को लपेटने के लिए अपने व्यूमोडेल में इतना कोड लिखना है।T4 templates पर एक नज़र डालें। कुछ डेवलपर इसका उपयोग अपने बॉयलरप्लेट कोड (यानी व्यू मॉडेल क्लास) को स्वतः उत्पन्न करने के लिए करते हैं। मैं इसे व्यक्तिगत रूप से उपयोग नहीं करता, मैं एक दृश्य कोड स्निपेट का उपयोग त्वरित रूप से व्यूमोडेल प्रॉपर्टी उत्पन्न करने के लिए करता हूं।

एक और विकल्प प्रिज्म या एमवीवीएमलाइट जैसे एमवीवीएम फ्रेमवर्क का उपयोग करना होगा। मैंने खुद का उपयोग नहीं किया है, लेकिन मैंने सुना है कि उनमें से कुछ ने बॉयलरप्लेट कोड को आसान बनाने के लिए सुविधाओं में बनाया है।

नोट करने के लिए एक और बिंदु यह है: यदि आप किसी डेटाबेस में अपनी सेटिंग्स संग्रहीत कर रहे हैं, तो डेटाबेस से अपने मॉडल जेनरेट करने के लिए एंटीटी फ्रेमवर्क जैसे ओआरएम फ्रेमवर्क का उपयोग करना संभव हो सकता है, जिसका अर्थ है कि आपने जो कुछ छोड़ा है वह है व्यूमोडेल और विचार।

+2

कोई जवाब नहीं था। अत्यधिक कोड के बारे में अपने प्रश्न के बारे में उपरोक्त मेरा संपादन देखें। – failedprogramming

+0

बहुत बहुत धन्यवाद, मुझे अब तक सब कुछ मिल रहा है :) – Xaser

+1

@Xaser यदि आप अपना प्रश्न स्टैक ओवरफ्लो पर पोस्ट करते हैं तो मैं पसंद करूंगा। आप इस तरह से और मदद प्राप्त करने में सक्षम हो जाएगा। आप मुझे किसी भी नए प्रश्न का लिंक भेज सकते हैं और मैं मदद करने की कोशिश करूंगा। धन्यवाद – failedprogramming

संबंधित मुद्दे