Showing posts with label mvvm. Show all posts
Showing posts with label mvvm. Show all posts

Monday, August 13, 2018

Selected item not working in Picker

Leave a Comment

In our edit page, we have an issue to populate the value into Selected Item in picker and it won't select for some reason from the LoadCourses() or LoadRoundCategories() either.

Any ideas?

Here's the code:

ViewModel

public class EditGolfRoundViewModel : INotifyPropertyChanged {     ApiServices _apiServices = new ApiServices();      private string _message;     private ObservableCollection<GolfCourse> _courses;     private ObservableCollection<GolfRoundCategory> _roundCategories;     private object_selectedGolfCourse;     private GolfRoundCategory _selectedGolfRoundCategory;     private GolfRound _golfRound;      public EditGolfRoundViewModel()     {         _selectedGolfCourse = new GolfCourse();         _selectedGolfRoundCategory = new GolfRoundCategory();         LoadCourses();         LoadRoundCategories();     }      public GolfRound GolfRound     {         get { return _golfRound; }         set         {             _golfRound = value;             OnPropertyChanged();         }     }       public string Message     {         get { return _message; }          set         {             _message = value;             OnPropertyChanged();         }     }      public ObservableCollection<GolfCourse> GolfCourses     {         get { return _courses; }         set         {             if (_courses != value)             {                 _courses = value;                 OnPropertyChanged();             }          }     }      public ObservableCollection<GolfRoundCategory> GolfRoundCategories     {         get { return _roundCategories; }         set         {             _roundCategories = value;             OnPropertyChanged();         }     }      public object SelectedGolfCourse     {         get { return _selectedGolfCourse; }         set         {             _selectedGolfCourse = value;             var golfCourse = _selectedGolfCourse as GolfCourse;             Guid tempGolfCourseID = golfCourse.GolfCourseID;             OnPropertyChanged("SelectedGolfCourse");         }     }       public GolfRoundCategory SelectedGolfRoundCategory     {         get { return _selectedGolfRoundCategory; }         set         {             _selectedGolfRoundCategory = value;             OnPropertyChanged();         }     }        public ICommand EditCommand      {          get         {             return new Command(async() =>             {                 GolfRound.GolfCourseID = SelectedGolfCourse.GolfCourseID;                 GolfRound.GolfCourse = SelectedGolfCourse;                 GolfRound.GolfRoundCategoryID = SelectedGolfRoundCategory.GolfRoundCategoryID;                 GolfRound.GolfRoundCategory = SelectedGolfRoundCategory;                 GolfRound.LastModifiedUTC = System.DateTime.Now;                  await _apiServices.PutGolfRoundAsync(GolfRound, Settings.AccessToken);             });         }      }      public ICommand DeleteCommand     {         get         {             return new Command(async () =>             {                 await _apiServices.DeleteGolfRoundAsync(GolfRound.GolfRoundID, Settings.AccessToken);             });         }      }      public event PropertyChangedEventHandler PropertyChanged;      [NotifyPropertyChangedInvocator]     protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)     {         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));     }      private async void LoadCourses()     {         GolfCourses = new ObservableCollection<GolfCourse>(await _apiServices.GetGolfCoursesAsync(Settings.AccessToken));     }      private async void LoadRoundCategories()     {         GolfRoundCategories = new ObservableCollection<GolfRoundCategory>(await _apiServices.GetGolfRoundCategoriesAsync(Settings.AccessToken));     }  } 

View - XAML

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"          xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"          xmlns:viewModels="clr-namespace:AthlosifyMobile.ViewModels.Golf"          x:Class="AthlosifyMobile.Views.EditGolfRoundPage">  <StackLayout Orientation="Vertical" VerticalOptions="Center" Spacing="30" Padding="30">     <Entry Text="{Binding GolfRound.Name}" Placeholder="name" FontSize="Default" />     <Entry Text="{Binding GolfRound.Notes}" Placeholder="notes" FontSize="Default" />     <Entry Text="{Binding GolfRound.DailyHandicap}" Placeholder="daily handicap" FontSize="Default" />     <Label Text="Date" />     <DatePicker Date="{Binding GolfRound.TeeOffUTC}"                  Format="D"                 Margin="30, 0, 0, 30"  />     <Picker x:Name="pCourse" Title="Course" ItemsSource="{Binding GolfCourses}"              SelectedItem="{Binding SelectedGolfCourse, Mode=TwoWay}"              ItemDisplayBinding="{Binding Name}"></Picker>     <Entry Text="{Binding GolfRound.GolfCourse.Name}" Placeholder="selected golf course" FontSize="Default" />     <Picker x:Name="pCategory" Title="Category" ItemsSource="{Binding GolfRoundCategories}"              SelectedItem="{Binding SelectedGolfRoundCategory, Mode=TwoWay}"             ItemDisplayBinding="{Binding Name}"></Picker>     <Entry Text="{Binding SelectedGolfRoundCategory.Name}" Placeholder="selected round category" FontSize="Default" />     <Button Command="{Binding EditCommand}" Text="Edit Round" />     <Button Command="{Binding DeleteCommand}" Text="Delete Round" />      <Label Text="{Binding Message}" ></Label> </StackLayout> 

View - code behind

public partial class EditGolfRoundPage : ContentPage {     public EditGolfRoundPage (GolfRound round)     {         var editGolfRoundViewModel = new EditGolfRoundViewModel();         editGolfRoundViewModel.GolfRound = round;          BindingContext = editGolfRoundViewModel;          InitializeComponent ();          //var editGolfRoundViewModel = new EditGolfRoundViewModel();         //editGolfRoundViewModel.GolfRound = round;         //editGolfRoundViewModel.SelectedGolfCourse = round.GolfCourse;          //BindingContext = editGolfRoundViewModel;      } } 

5 Answers

Answers 1

Implement IEquatable for class of property used in SelectedItem:

public class GolfCourse : IEquatable<GolfCourse> {     ...     public bool Equals(GolfCourse other)     {         if (other == null) return false;         return (this.Name.Equals(other.Name));     } } 

Usage, assuming ItemsSource contains an object with value of Name as shown below:

SelectedGolfCourse = new GolfCourse { Name = "Course 2" }; 

Answers 2

you are binding view model before Initialise page so that is wrong thing we can not bind data without Initialise page fro that you need to change code of xaml.cs like below public EditGolfRoundPage (GolfRound round) { InitializeComponent (); BindingContext = editGolfRoundViewModel; BindingContext.GolfRound = round; }

that will work for you Happy Coding :)

Answers 3

Xaml

    <Picker x:Name="ProductPicker" WidthRequest="220"    HeightRequest="35" Title="Select" ItemsSource="{Binding ProductList}" SelectedItem="{Binding ProductSelected}" ItemDisplayBinding="{Binding ProductName}">   </Picker> 

ViewModel

 public List<ProductModel> ProductList { get; set; } 

Populating Data in Datasource in Viewmodel

    ProductList = Products.Result.ToList(); 

Getting Selected Data

   private object _ProductSelected;       public object ProductSelected     {         get { return _ProductSelected; }         set         {             _ProductSelected = value;              ProductSelected_SelectedIndex.Execute(value);              OnPropertyChanged("ProductSelected"); //in case you are using MVVM Light         }     }   private Command ProductSelected_SelectedIndex     {         get         {             return new Command((e) =>             {             }}}     private object _CitySelectedFromList;     public object CitySelectedFromList     {         get { return _CitySelectedFromList; }         set         {             _CitySelectedFromList = value;             var cityid = _CitySelectedFromList as CityMasterModel;             tempcityids = Convert.ToInt32(cityid.Id);          }     } 

Answers 4

Can you try once replacing your Viewmodel. I have changed the type from Object to actual type. Set the Default item while loading the items from endpoint.

public class EditGolfRoundViewModel : INotifyPropertyChanged { ApiServices _apiServices = new ApiServices();  private string _message; private ObservableCollection<GolfCourse> _courses; private ObservableCollection<GolfRoundCategory> _roundCategories; private GolfCourse _selectedGolfCourse; private GolfRoundCategory _selectedGolfRoundCategory; private GolfRound _golfRound;  public EditGolfRoundViewModel() {     LoadCourses();     LoadRoundCategories(); }  public GolfRound GolfRound {     get { return _golfRound; }     set     {         _golfRound = value;         OnPropertyChanged();     } }   public string Message {     get { return _message; }      set     {         _message = value;         OnPropertyChanged();     } }  public ObservableCollection<GolfCourse> GolfCourses {     get { return _courses; }     set     {         if (_courses != value)         {             _courses = value;             OnPropertyChanged();         }      } }  public ObservableCollection<GolfRoundCategory> GolfRoundCategories {     get { return _roundCategories; }     set     {         _roundCategories = value;         OnPropertyChanged();     } }  public GolfCourse SelectedGolfCourse {     get { return _selectedGolfCourse; }     set     {         _selectedGolfCourse = value;         OnPropertyChanged("SelectedGolfCourse");     } }   public GolfRoundCategory SelectedGolfRoundCategory {     get { return _selectedGolfRoundCategory; }     set     {         _selectedGolfRoundCategory = value;         OnPropertyChanged();     } }    public ICommand EditCommand {     get     {         return new Command(async () =>         {             GolfRound.GolfCourseID = SelectedGolfCourse.GolfCourseID;             GolfRound.GolfCourse = SelectedGolfCourse;             GolfRound.GolfRoundCategoryID = SelectedGolfRoundCategory.GolfRoundCategoryID;             GolfRound.GolfRoundCategory = SelectedGolfRoundCategory;             GolfRound.LastModifiedUTC = System.DateTime.Now;              await _apiServices.PutGolfRoundAsync(GolfRound, Settings.AccessToken);         });     }  }  public ICommand DeleteCommand {     get     {         return new Command(async () =>         {             await _apiServices.DeleteGolfRoundAsync(GolfRound.GolfRoundID, Settings.AccessToken);         });     }  }  public event PropertyChangedEventHandler PropertyChanged;  [NotifyPropertyChangedInvocator] protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }  private async void LoadCourses() {     GolfCourses = new ObservableCollection<GolfCourse>(await _apiServices.GetGolfCoursesAsync(Settings.AccessToken));     if (GolfCourses != null && GolfCourses.Count() > 0)         SelectedGolfCourse = GolfCourses[0]; }  private async void LoadRoundCategories() {     GolfRoundCategories = new ObservableCollection<GolfRoundCategory>(await _apiServices.GetGolfRoundCategoriesAsync(Settings.AccessToken));     if (GolfRoundCategories != null && GolfRoundCategories.Count() > 0)         SelectedGolfRoundCategory = GolfRoundCategories[0];  }  } 

Answers 5

You can try:

  • make sure your prop support INotifyPropertyChanged
  • make sure Selected Item variable has initial value
  • try debug your selected item variable, make sure has value

1. Initials

Take a look your code:

public EditGolfRoundViewModel() {     _selectedGolfCourse = new GolfCourse();     _selectedGolfRoundCategory = new GolfRoundCategory();     LoadCourses();     LoadRoundCategories(); } 

If you try to initial value Selected Item, don't do this:

_selectedGolfCourse = new GolfCourse(); _selectedGolfRoundCategory = new GolfRoundCategory(); 

let it null, is fine. You can do like this:

SelectedGolfRoundCategory = new GolfRoundCategory(); //or SelectedGolfRoundCategory = dataFromAPI; 

2. Assign

Take a look your code:

public ICommand EditCommand  {      get     {         return new Command(async() =>         {             GolfRound.GolfCourseID = SelectedGolfCourse.GolfCourseID;             GolfRound.GolfCourse = SelectedGolfCourse;             GolfRound.GolfRoundCategoryID = SelectedGolfRoundCategory.GolfRoundCategoryID;             GolfRound.GolfRoundCategory = SelectedGolfRoundCategory;             GolfRound.LastModifiedUTC = System.DateTime.Now;              await _apiServices.PutGolfRoundAsync(GolfRound, Settings.AccessToken);         });     }  } 

You trying selected item variable insert into to object GolfRound, like this part:

  GolfRound.GolfRoundCategoryID = SelectedGolfRoundCategory.GolfRoundCategoryID;   GolfRound.GolfRoundCategory = SelectedGolfRoundCategory; 

Make sure you have INotifyPropertyChanged implement this model GolfRound for prop GolfRoundCategoryID and GolfRoundCategory. If not, it would not work. I have experience for this.

Hope this helpful.

Read More

Sunday, April 29, 2018

Two way data binding issue in a MVVM

Leave a Comment

I am trying to build a simple C#/WinFoms project that uses the Model-View-ViewModel design pattern according to the structure:

enter image description here

The data binding between the two UserControls and the associated ViewModels does not work well.

The MainForm contains two UserControls (UC): Uc_Create, Uc_Iteration. Each UC contains a combobox that is connected to the associated property in the ViewModel_xxx, namely

Uc_Create has:

this.comboBox1ComplexCreate.DataSource = oVM_Create.VM_Create_ListOfStringsInModel;  

Uc_Iteration has:

this.comboBox1ComplexIteration.DataSource = oVM_Iteration.VM_Iteration_ListOfStringsInModel; 

The problem:

When I add elements to VM_Iteration_ListOfStringsInModel the combobox in the corresponding UC (comboBox1ComplexCreate) and the list in the Model are correctly changed but the other combobox (comboBox1ComplexIteration) in Uc_Iteration is not!

Why????

If I change the List in the Model to a BindingList, everything works fine. What am I doing wrong?

Thanks in advance!


Model:

namespace Small_MVVM {      public class Model      {         private static readonly object m_oLock = new object();          private static Model instance;           public List<string> simplelistOfStrings;           private Model()         {             simplelistOfStrings = new List<string>();         }          public static Model GetInstance()         {             if (instance == null)             {                 lock (m_oLock)                 {                     if (instance == null)                     {                         instance = new Model();                     }                 }             }             return instance;         }     } } 

ModelView_Create:

namespace Small_MVVM {     class ViewModel_Create : NotifyPropertyChangedBase     {         private static Model oModel = Model.GetInstance();          private BindingList<string> _VM_Create_ListOfStringsInModel = new BindingList<string>(oModel.simplelistOfStrings);         public BindingList<string> VM_Create_ListOfStringsInModel         {             get             {                  return _VM_Create_ListOfStringsInModel;             }             set             {                 _VM_Create_ListOfStringsInModel = value;                 this.FirePropertyChanged(nameof(VM_Create_ListOfStringsInModel));             }         }     } } 

ModelView_Iteration:

namespace Small_MVVM {      class ViewModel_Iteration : NotifyPropertyChangedBase     {         private static Model oModel = Model.GetInstance();          public ViewModel_Iteration()         {          }          BindingList<string> _VM_Iteration_ListOfStringsInModel = new BindingList<string>(oModel.simplelistOfStrings);         public BindingList<string> VM_Iteration_ListOfStringsInModel         {             get             {                 return _VM_Iteration_ListOfStringsInModel;             }             set             {                 _VM_Iteration_ListOfStringsInModel = value;                 this.FirePropertyChanged(nameof(VM_Iteration_ListOfStringsInModel));             }         }     } } 

This is the abstract class NotifyPropertyChangedBase that implements the INotifyPropertyChange interface:

public abstract class NotifyPropertyChangedBase : INotifyPropertyChanged {      public event PropertyChangedEventHandler PropertyChanged;      protected bool CheckPropertyChanged<T>(string propertyName, ref T oldValue, ref T newValue)     {             if (oldValue == null && newValue == null)         {                 return false;         }          if ((oldValue == null && newValue != null) || !oldValue.Equals((T)newValue))         {             oldValue = newValue;             return true;         }          return false;     }      private delegate void PropertyChangedCallback(string propertyName);      protected void FirePropertyChanged(string propertyName)     {          if (this.PropertyChanged != null)         {             this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));         }     } } 

3 Answers

Answers 1

If you are following MVVM pattern , then you should make use of ObservableCollection for collections like as below

   private ObservableCollection<int> _intList;    public ObservableCollection<int> IntList      {         get         {           return _intList;         }         set         {            _intList= value;            _intList.CollectionChanged +=                          new System.Collections.Specialized.NotifyCollectionChangedEventHandler                                             (MyProperty_CollectionChanged);             }         }   void MyProperty_CollectionChanged(object sender,                                  System.Collections.Specialized.NotifyCollectionChangedEventArgs e)   {      NotifyPropertyChanged("IntList");  } 

ObservableCollection - Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.

you can also check this : MVVM (Model-View-ViewModel) Pattern For Windows Form Applications, using C#

Answers 2

I think your problem is that there is no link between your two BindingLists.

When you add items to VM_Iteration_ListOfStringsInModel the changes are propagated to the bound combobox and the underlying List in your model. But the list in your model cannot further propagate these changes to VM_Create_ListOfStringsInModel as it does not support this feature. VM_Create_ListOfStringsInModel will contain the item you added to VM_Iteration_ListOfStringsInModel but it won't raise a ListChanged event as List in you model breaks the event chain. A List cannot raise an ListChanged event. That is what BindingList exists for.

And as you already tried, when you replace the list in your model by a BindingList it works.

So one solution you have already mentioned, the other would be to use only one shared BindingList for both comboboxes if this is an option for you.

    private void ListDemo()     {          var l = new List<string>();         l.Add("A");          BindingList<string> blist1 = new BindingList<string>(l);         BindingList<string> blist2 =new BindingList<string>(l);         blist1.ListChanged += Blist1_ListChanged;         blist2.ListChanged += Blist2_ListChanged;           blist1.Add("B");         // at this point blist1 and blist2 items count is 2 but only blist1 raised ListChanged     }      private void Blist2_ListChanged(object sender, ListChangedEventArgs e)     {         //No event fired here when adding B     }      private void Blist1_ListChanged(object sender, ListChangedEventArgs e)     {        // event fired when adding B     } 

Answers 3

Both ViewModel_Iteration and ViewModel_Create defines their property (VM_Iteration_ListOfStringsInModel and VM_Create_ListOfStringsInModel) by initializing a new object of BindingList which uses the model.simplelistOfStrings as specified source list. So both ViewModels have different object of ListOfStringsInModel --- They don't point to same object.

Definitely you should define the simplelistOfStrings property as BindingList so that a two-way data-binding mechanism can be established in your View and code behind. However, instead of defining a new member variable in both ViewModel_Iteration and ViewModel_Create ViewModels, I would suggest that you should change the property definition as follows :

        public BindingList<string> VM_Iteration_ListOfStringsInModel         {             get             {                 return oModel.simplelistOfStrings;             }             set             {                 oModel.simplelistOfStrings = value;                 this.FirePropertyChanged(nameof(VM_Iteration_ListOfStringsInModel));             }         } 

And

        public BindingList<string> VM_Create_ListOfStringsInModel         {             get             {                  return oModel.simplelistOfStrings;             }             set             {                 oModel.simplelistOfStrings = value;                 this.FirePropertyChanged(nameof(VM_Create_ListOfStringsInModel));             }         } 

Another improvement in above approach can be done is that don't use the set at all property definition at all. The Notification is required as reference of property will change when assigned with a new List. So instead of allowing to set a new list, use a approach to Clear and Add new items in the list. That's way the reference of property will remain the same and Two way binding would work without pluming the explicit notification.

ViewModel_Iteration

            public BindingList<string> VM_Iteration_ListOfStringsInModel             {                 get                 {                     return oModel.simplelistOfStrings;                 }             } 

ViewModel_Create

            public BindingList<string> VM_Create_ListOfStringsInModel             {                 get                 {                      return oModel.simplelistOfStrings;                 }             } 

Usage

VM_Create_ListOfStringsInModel.Clear(); VM_Create_ListOfStringsInModel.Add(item); // Or use AddRange to add Range. 
Read More

Monday, March 19, 2018

Preserve theme style of control while modifying the control template

Leave a Comment

I'm trying to add bind some commands to Slider.Thumb and this is how I currently do it:

<Style x:Key="BasicSliderStyle" TargetType="{x:Type customControls:ThumbDragSlider}" BasedOn="{StaticResource {x:Type Slider}}">     <Setter Property="Template">         <Setter.Value>             <ControlTemplate TargetType="{x:Type Slider}">                 <Grid>                     <Track x:Name="PART_Track">                         <Track.Thumb>                             <Thumb x:Name="Thumb">                                 <i:Interaction.Triggers>                                     <i:EventTrigger EventName="MouseEnter">                                         <command:EventToCommand Command="{Binding PositionSliderThumbMouseEnterCommand}" PassEventArgsToCommand="True"/>                                     </i:EventTrigger>                                     <i:EventTrigger EventName="DragDelta">                                         <command:EventToCommand Command="{Binding PositionThumbDragDeltaCommand}" CommandParameter="{Binding ElementName=sMovieSkipSlider}"/>                                     </i:EventTrigger>                                 </i:Interaction.Triggers>                             </Thumb>                         </Track.Thumb>                     </Track>                 </Grid>             </ControlTemplate>         </Setter.Value>     </Setter> </Style> 

The problem is that I also have a theme applied to my controls and if I use this style the style and effects from it are removed as well, is there a way to keep the current application theme style on a control while also being able to add event triggers and attach commands as show above?

P.S - Since this is a custom control that derives from Slider, this is how I set the default theme style for the slider to it

Style="{StaticResource {x:Type Slider}}" 

2 Answers

Answers 1

You can only use Triggers in the Style and not in the Template, if you want to keep the same theme for that control. That means that it is not possible in your case. What you can do is copy the entire Style of your theme and specify the trigger there.

Answers 2

You can extend your custom style. Instead of:

BasedOn="{StaticResource {x:Type Slider}}" 

do:

BasedOn="{StaticResource MyCustomSlider}" 
Read More

Friday, December 22, 2017

Pause/prevent animation for a checkbox control

Leave a Comment

I have a checkbox substituting a switch-like control.

enter image description here

enter image description here

It works great. The only problem is that this checkbox initial mode can be either true or false. For false - no problem, but if it's true, then when the view is loaded, you immediately see the animation of the switch moving.

I want to prevent that. Is there anyway to do so?

Here's the relevant XAML:

<CheckBox Style="{StaticResource MySwitch}"  IsChecked="{Binding ExplicitIncludeMode}" ></CheckBox>  <Style x:Key="MySwitch" TargetType="{x:Type CheckBox}">     <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>     <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>     <Setter Property="Template">         <Setter.Value>             <ControlTemplate TargetType="{x:Type CheckBox}">                 <ControlTemplate.Resources>                     <Storyboard x:Key="OnChecking">                         <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">                             <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="55"/>                         </DoubleAnimationUsingKeyFrames>                     </Storyboard>                     <Storyboard x:Key="OnUnchecking">                         <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="slider" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">                             <SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>                         </DoubleAnimationUsingKeyFrames>                         <ThicknessAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="slider" Storyboard.TargetProperty="(FrameworkElement.Margin)">                             <SplineThicknessKeyFrame KeyTime="00:00:00.3000000" Value="1,1,1,1"/>                         </ThicknessAnimationUsingKeyFrames>                     </Storyboard>                 </ControlTemplate.Resources>                  <DockPanel x:Name="dockPanel">                     <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" ContentTemplate="{TemplateBinding ContentTemplate}" RecognizesAccessKey="True" VerticalAlignment="Center"/>                     <Border BorderBrush="LightGray" BorderThickness="1" Margin="5,5,0,5">                         <Grid  Width="110" Background="GhostWhite">                             <TextBlock Text="Included" TextWrapping="Wrap" FontWeight="Medium" FontSize="12" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,0,3,0" Foreground="#FF00AFC4"/>                             <TextBlock HorizontalAlignment="Left" Margin="2,0,0,0" FontSize="12" FontWeight="Bold" Text="Excluded" VerticalAlignment="Center" TextWrapping="Wrap" Foreground="#FFE4424D"/>                             <Border HorizontalAlignment="Left" x:Name="slider" Width="55" BorderThickness="1,1,1,1" CornerRadius="3,3,3,3" RenderTransformOrigin="0.5,0.5" Margin="1,1,1,1">                                 <Border.RenderTransform>                                     <TransformGroup>                                         <ScaleTransform ScaleX="1" ScaleY="1"/>                                         <SkewTransform AngleX="0" AngleY="0"/>                                         <RotateTransform Angle="0"/>                                         <TranslateTransform X="0" Y="0"/>                                     </TransformGroup>                                 </Border.RenderTransform>                                 <Border.BorderBrush>                                     <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">                                         <GradientStop Color="WhiteSmoke" Offset="0"/>                                         <GradientStop Color="#FFFFFFFF" Offset="1"/>                                     </LinearGradientBrush>                                 </Border.BorderBrush>                                 <Border.Background>                                     <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">                                         <GradientStop x:Name="grdColor" Color="#FF00AFC4" Offset="1"/>                                         <GradientStop Color="#092E3E" Offset="0"/>                                     </LinearGradientBrush>                                 </Border.Background>                             </Border>                         </Grid>                     </Border>                 </DockPanel>                  <ControlTemplate.Triggers>                     <MultiTrigger>                         <MultiTrigger.Conditions>                             <Condition Property="IsChecked" Value="True"/>                             <Condition Property="IsPressed" Value="True"/>                         </MultiTrigger.Conditions>                          <MultiTrigger.ExitActions>                             <BeginStoryboard Storyboard="{StaticResource OnUnchecking}" x:Name="OnUnchecking_BeginStoryboard"/>                         </MultiTrigger.ExitActions>                         <MultiTrigger.EnterActions>                             <BeginStoryboard Storyboard="{StaticResource OnChecking}" x:Name="OnChecking_BeginStoryboard"/>                         </MultiTrigger.EnterActions>                     </MultiTrigger>                     <Trigger Property="IsEnabled" Value="False">                         <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>                     </Trigger>                 </ControlTemplate.Triggers>             </ControlTemplate>         </Setter.Value>     </Setter>     <Setter Property="Width" Value="118"></Setter>     <Setter Property="Height" Value="39"></Setter>  </Style> 

This is the way I initialize the view + viewmodel:

// ctor of view (tab) public MonitoredExtensions() {     InitializeComponent();     DataContext = new MonitoredExtensionsViewModel(); }  // ctor of viewmodel public MonitoredExtensionsViewModel() {      ...      ExplicitIncludeMode = true/false;      ... } 

1 Answers

Answers 1

Found 1 way to do this.

Turns out you can bind the initial X location of the switch/slider. So I binded it to a property in the ViewModel, and updated it.

Changes to XAML:

<TranslateTransform X="{Binding InitialPosition}" Y="0"/> 

Ctor of View:

public MonitoredExtensions() {     InitializeComponent();     DataContext = new MonitoredExtensionsViewModel(); } 

Ctor of ViewModel:

public MonitoredExtensionsViewModel() {      ...      ExplicitIncludeMode = true/false;      InitialPosition = (ExplicitIncludeMode) ? 55 : 0;      ... } 

More from the ViewModel:

public Double InitialPosition { get; set; } 

So as the view is loaded, the viewmodel is being created, and the original/initial location of the slider is calculated according to the checkbox state.

Read More

Thursday, August 3, 2017

List Box items with checkboxes, multiselect not working properly in WPF MVVM

Leave a Comment

So I have a ListBox with CheckBox-es that have the IsChecked property bound to the Item's property called IsSelected. That produces a weird behavior where if I click on the item itself it checks the checkbox (good) and sets the property on the item (good), but doesn't actually select the item in the list box, ie. the highlighting isn't there. I am guessing that the ListBox IsSelected property needs to be set as well for that right? Now, I am trying to get the multi-select behavior to work so I changed the SelectionMode to Extended. Now, I can select only Items, not the checkboxes. What happens is that if I use SHIFT + click by pointing at the area next to the item, not the item itself, then it select multiple items, but clicking on the items themselves doesn't do the trick of multi-selection not does it check the checkboxes. What is going on in here?

I would like to be able to select multiple items by holding shift etc, and have that trigger the property on the Elevation item so I know which ones are checked. Any help is appreciated.

Here's my XAML:

<ListBox x:Name="LevelsListBox"                          ItemsSource="{Binding Elevations, UpdateSourceTrigger=PropertyChanged}"                          SelectionMode="Extended"                          BorderThickness="0">                     <ListBox.ItemTemplate>                         <DataTemplate>                             <CheckBox IsChecked="{Binding IsSelected}" Content="{Binding Name}"/>                         </DataTemplate>                     </ListBox.ItemTemplate>                 </ListBox> 

My View Model:

public class AxoFromElevationViewModel : ViewModelBase     {         public AxoFromElevationModel Model { get; }         public RelayCommand CheckAll { get; }         public RelayCommand CheckNone { get; }          public AxoFromElevationViewModel(AxoFromElevationModel model)         {             Model = model;             Elevations = Model.CollectElevations();             CheckAll = new RelayCommand(OnCheckAll);             CheckNone = new RelayCommand(OnCheckNone);         }           private void OnCheckNone()         {             foreach (var e in Elevations)             {                 e.IsSelected = false;             }         }           private void OnCheckAll()         {             foreach (var e in Elevations)             {                 e.IsSelected = true;             }         }          /// <summary>         /// All Elevation Wrappers.         /// </summary>         private ObservableCollection<ElevationWrapper> _elevations = new ObservableCollection<ElevationWrapper>();         public ObservableCollection<ElevationWrapper> Elevations         {             get { return _elevations; }             set { _elevations = value; RaisePropertyChanged(() => Elevations); }         }     } 

Finally my Elevation Class:

public sealed class ElevationWrapper : INotifyPropertyChanged     {         public string Name { get; set; }         public ElementId Id { get; set; }         public object Self { get; set; }          private bool _isSelected;         public bool IsSelected         {             get { return _isSelected; }             set { _isSelected = value; RaisePropertyChanged("IsSelected"); }         }          public ElevationWrapper(View v)         {             Name = v.Name;             Id = v.Id;             Self = v;             IsSelected = false;         }          public event PropertyChangedEventHandler PropertyChanged;         private void RaisePropertyChanged(string propname)         {             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));         }     } 

2 Answers

Answers 1

You should bind the IsSelected property of your ListBoxItems to the IsSelected property of your view model. This way CheckBoxes will trigger the selection and when you select an item, the related CheckBox will be checked.

<ListBox.ItemContainerStyle>     <Style TargetType="ListBoxItem">         <Setter Property="IsSelected" Value="{Binding IsSelected}"/>     </Style> </ListBox.ItemContainerStyle> 

Answers 2

It seems to me you want to sync 3 properties ListBoxItem.IsSelected, CheckBox.IsChecked and your models IsSelected. My advice is that only one of the templates/styles should bind to the underlying model so I will add Yusuf answer as I will use the ListBoxItem style to bind to your model property. After that you should bind the Checkbox.IsChecked to the ListBoxItem.IsSelected and your ListBox should look like this:

    <ListBox x:Name="LevelsListBox"              ItemsSource="{Binding Elevations, UpdateSourceTrigger=PropertyChanged}"              SelectionMode="Extended"              BorderThickness="0">         <ListBox.ItemContainerStyle>             <Style TargetType="ListBoxItem">                 <Setter Property="IsSelected" Value="{Binding IsSelected}"/>             </Style>         </ListBox.ItemContainerStyle>         <ListBox.ItemTemplate>             <DataTemplate>                 <CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" Content="{Binding Name}"/>             </DataTemplate>         </ListBox.ItemTemplate>     </ListBox> 

Always try to bind XAML properties in a chain way, e.g. model.A binds to Model.B binds to Model.C, doing this should help you keep updates consistent and avoid wierd cases.

There is an issue with this code though, after you select multiple items and click one check box it will only unselect that item but if you click another item it will unselect all except that item.

Read More

Sunday, April 17, 2016

Set UpdateSourceTrigger to Explicit for WPF ListBox Item Source Controls using MVVM C#

Leave a Comment

I'm having a Collection of type Employee Model Class. It has two properties empName and isChecked.

Expectation : I need to update the property isChecked from the Checkbox, while on Clicking the Apply Button. Otherwise it don't need to update the Property.

void Main() {     Dictionary<int, List<Employee>> empList = new Dictionary<int, List<Employee>>()     {         {1, new List<Employee>() { new Employee() {empName = "Raj"}, new Employee() {empName = "Kumar"}}},         {2, new List<Employee>() { new Employee() {empName = "Bala"}}},          {3, new List<Employee>() { new Employee() {empName = "Manigandan"}}},          {4, new List<Employee>() { new Employee() {empName = "Prayag"}, new Employee() {empName = "Pavithran"}}},          {5, new List<Employee>() { new Employee() {empName = "Selva"}}},     };      empList.Dump(); }  public class Employee {     public string empName { get; set; }     public bool isChecked { get; set; } } 

I Binded this Collection into a WPF ListBox using MVVM approach. The empName is Binded with TextBlock and isChecked is Binded with Checkbox.

<ListBox ItemsSource="{Binding empList.Values, IsAsync=True, UpdateSourceTrigger=Explicit}">     <cust:BListBox.ItemTemplate>         <DataTemplate>              <CheckBox IsChecked="{Binding isChecked, UpdateSourceTrigger=Explicit}">                  <CheckBox.Content>                      <StackPanel Orientation="Horizontal">                          <TextBlock Text="{Binding empName, IsAsync=True}" Visibility="Visible" />                       </StackPanel>                   </CheckBox.Content>               </CheckBox>         </DataTemplate>     </ListBox.ItemTemplate> </ListBox>  <Button Content="Apply" Command="{Binding ApplyChangesCommand}"/> 

The Command for the Apply Button is

public ICommand ApplyChangesCommand         {             get             {                 return new DelegatingCommand((object param) =>                 {                     /// Logical Code.                  });             }         } 

Note: Kindly use MVVM approach.

3 Answers

Answers 1

There is my suggestion:
At first, you should define EmployeesViewModel

public class EmployeesViewModel : INotifyPropertyChanged {     public ObservableCollection<EmployeeViewModel> EmployeeList { get; set; }      public ICommand ApplyChangesCommand;      public EmployeesViewModel()     {         EmployeeList = new ObservableCollection<EmployeeViewModel>         {              new EmployeeViewModel(new Employee {EmpName = "Raj"}),             new EmployeeViewModel(new Employee {EmpName = "Kumar"}),             new EmployeeViewModel(new Employee {EmpName = "Bala"}),             new EmployeeViewModel(new Employee {EmpName = "Manigandan"}),             new EmployeeViewModel(new Employee {EmpName = "Prayag"}),             new EmployeeViewModel(new Employee {EmpName = "Pavithran"}),             new EmployeeViewModel(new Employee {EmpName = "Selva"})         };          ApplyChangesCommand = new DelegatingCommand(ApplyChanges);     }      private void ApplyChanges(object param)     {         foreach(var item in EmployeeList)         {             item.Model.IsChecked = item.IsChecked;         }     }     .... } 

This view model contains the itemssource and selected item of the ListBox. The ApplyChanges(object par) method is called when the ApplyChangesCommand is invoked and the Employee.IsChecked is updated.

You should also wrap the Employee into the view model.

public class EmployeeViewModel : INotifyPropertyChanged {     public Employee Model { get; set; }      private string _empName;      public string EmpName     {         get { return _empName;}         set         {             _empName = value;             OnPropertyChanged();         }     }      private bool _isChecked;      public bool IsChecked     {         get { return _isChecked;}         set         {             _isChecked = value;             OnPropertyChanged();         }     }      public EmployeeViewModel(Employee model)     {         Model = model;         IsChecked = model.IsChecked;         EmpName = model.EmpName;     }      public event PropertyChangedEventHandler PropertyChanged;      [NotifyPropertyChangedInvocator]     protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)     {         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));     } } 

Suppose that the DataContext of the ListBox is EmployeesViewModel.

<ListBox ItemsSource="{Binding EmployeeList, IsAsync=True, UpdateSourceTrigger=Explicit}" SelectedItem="{Binding SelectedItem}"> <cust:BListBox.ItemTemplate>     <DataTemplate>          <CheckBox IsChecked="{Binding IsChecked, UpdateSourceTrigger=Explicit}">              <CheckBox.Content>                  <StackPanel Orientation="Horizontal">                      <TextBlock Text="{Binding EmpName, IsAsync=True}" Visibility="Visible" />                   </StackPanel>               </CheckBox.Content>           </CheckBox>     </DataTemplate> </ListBox.ItemTemplate> 

Answers 2

Ok, so I've found a solution. This solution isn't too generic, but it can be made generic via some simple steps. Tell me if it's important to you.

WARNING! It's going to be a bit long...

For this solution, we will use the following:

  • VisualTreeHelper to get all CheckBox items in VisualTree. (Credit: Used THIS solution.)
  • A Behavior to update all check boxes in the ListBox.

So, let's begin!

THE HELPER

As I've states, I've took the solution from THIS answer.

Create a helper class with a FindVisualChildren method:

public static class VisualHelper {     public static IEnumerable<T> FindVisualChildren<T>(DependencyObject dependencyObject) where T: DependencyObject     {         if (dependencyObject == null)             yield break;          int totalChildrenAmount = VisualTreeHelper.GetChildrenCount(dependencyObject);         for (int childIndex = 0; childIndex < totalChildrenAmount; childIndex++)         {             DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, childIndex);             if (child is T)             {                 yield return (T)child;             }              foreach (T deeperChild in FindVisualChildren<T>(child))             {                 yield return deeperChild;             }         }     } } 

This method will help us to get all the CheckBoxes that are under the ListBox control.

THE BEHAVIOR

ApplyAllCheckBoxBindingsInListBoxBehavior. I know this name is long, but since we need something very specific, I strongly suggest to use a long name, to make it clear what the behavior does.

I've switched it from Command to a behavior, since IMHO, since commands are initialized from the ViewModel, the command shouldn't have any references to visuals (controls and such) and the solution is based on accessing visual controls.

Enough talk, here is the behavior:

public class ApplyAllCheckBoxBindingsInListBoxBehavior {     public static ListBox GetListBox(DependencyObject obj)     {         return (ListBox)obj.GetValue(ListBoxProperty);     }      public static void SetListBox(DependencyObject obj, ListBox value)     {         obj.SetValue(ListBoxProperty, value);     }      // Using a DependencyProperty as the backing store for ListBox.  This enables animation, styling, binding, etc...     public static readonly DependencyProperty ListBoxProperty =         DependencyProperty.RegisterAttached("ListBox", typeof(ListBox), typeof(ApplyBindingsBehavior), new PropertyMetadata(null, ListBoxChanged));      private static void ListBoxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)     {         Button button = d as Button;         if (button == null)             return;          button.Click -= OnClick;         button.Click += OnClick;     }      private static void OnClick(object sender, RoutedEventArgs routedEventArgs)     {         ListBox lb = GetListBox(sender as Button);         IEnumerable<CheckBox> allCBs = VisualHelper.FindVisualChildren<CheckBox>(lb);          foreach (CheckBox checkBox in allCBs)         {             checkBox.GetBindingExpression(CheckBox.IsCheckedProperty).UpdateSource();         }     } } 

The behavior will be set on the button in the XAML:

<ListBox Name="EmpList" ItemsSource="{Binding empList.Values, IsAsync=True, UpdateSourceTrigger=Explicit}">     <cust:BListBox.ItemTemplate>         <DataTemplate>              <CheckBox IsChecked="{Binding isChecked, UpdateSourceTrigger=Explicit}">                  <CheckBox.Content>                      <StackPanel Orientation="Horizontal">                          <TextBlock Text="{Binding empName, IsAsync=True}" Visibility="Visible" />                       </StackPanel>                   </CheckBox.Content>               </CheckBox>         </DataTemplate>     </ListBox.ItemTemplate> </ListBox>  <Button Content="Apply" behaviors:ApplyAllCheckBoxBindingsInListBoxBehavior.ListBox="{Binding ElementName=EmpList}"/> 

Note that I've added a name to the ListBox and replaced the Command on the button with the behavior.

As I've stated before, the behavior is very specific. This is to make the solution simpler. If you want a more generic behavior, the behavior, the XAML and the helper needs to be modified a bit.

First of all, make this solution work. If it works, and you still want to make it more generic, let me know, I'll be glad to help!

Happy Coding! :)

Answers 3

Your real problem is UpdateSourceTrigger=Explicit now needs the UpdateSource() method of BindingExpression to be called. So there is no way it can be achieved by only binding. And you want MVVM solution so you also don't want any UI objects in ViewModel.

So the question becomes how will you get access to UpdateSource() of all bindings in ViewModel?

here is how I've done it.

Create a CustomControl of Button which will hold your BindingExpression which we can later pass in Command:

public class MyButton : Button {     private List<BindingExpression> bindings;     public List<BindingExpression> Bindings     {         get          {             if (bindings == null)                 bindings = new List<BindingExpression>();             return bindings;          }         set { bindings = value; }     } } 

Command:

public RelayCommand ApplyChangesCommand { get; set; }     public void ApplyChangesCommandAction(object param)     {         foreach (var item in (param as List<BindingExpression>))         {             item.UpdateSource();         }     } 

Command Binding:

 <local:MyButton x:Name="MyButton" Content="Apply" Command="{Binding ApplyChangesCommand}"                      CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=Bindings}"/> 

At last the tricky part(which we can/will debate) to associate your all bindings to Button's collection. I've created a Converter to do so(not really to bind any command), you can use any other event/behaviour etc:

Command binding for Check Box:

<CheckBox.Command>     <MultiBinding Converter="{StaticResource Converter}">                                        <Binding RelativeSource="{RelativeSource Self}" Path="." />        <Binding ElementName="MyButton" Path="Bindings" />     </MultiBinding> </CheckBox.Command> 

Converter:

 public class Converter : IMultiValueConverter {     public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)     {         CheckBox FE = values[0] as CheckBox;         List<BindingExpression> bindings = values[1] as List<BindingExpression>;         bindings.Add(FE.GetBindingExpression(CheckBox.IsCheckedProperty));         return null;     }      public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)     {         return null;     } } 

And Everything Works Fine.

Read More

Thursday, April 14, 2016

How to load content properly inside a ModernWindow using mvvm

Leave a Comment

At our company, we're used to develop our applications using WinForms, Now we decided to switch to WPF-MVVM with Caliburn.Micro and Modern UI. The thing we're trying to reach is to have a small application that has: - 1 Modern Window - 2 Pages inside that Modern window the goal is to have a button inside that page that navigates the Modern Window to the second page with parameters.

I've been working trying to understand how to work this out, I succeeded with the Window (without the MUI), but when it comes to MUI, it's not really giving me the result we want.

So far, All I did, is

  1. Create a new MUI Project
  2. Add the Caliburn.Micro to the project
  3. Change the App.xaml to

    <Application x:Class="MuiWithCaliburn01.App"          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"          xmlns:local="clr-namespace:MuiWithCaliburn01"> <Application.Resources>     <ResourceDictionary>         <ResourceDictionary.MergedDictionaries>             <ResourceDictionary>                 <local:AppBootstrapper x:Key="bootstrapper" />                 <local:ModernContentLoader x:Key="ModernContentLoader" />             </ResourceDictionary>             <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" />             <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Light.xaml"/>         </ResourceDictionary.MergedDictionaries>     </ResourceDictionary> </Application.Resources> 

  4. Create the Bootstrapper Class

     public class AppBootstrapper : BootstrapperBase {     static AppBootstrapper()     {     }     public AppBootstrapper()     {         Initialize();     }     protected override void OnStartup(object sender, StartupEventArgs e)     {         DisplayRootViewFor();     } } 
  5. Create the ModernContentLoader Class

     public class ModernContentLoader : DefaultContentLoader {     protected override object LoadContent(Uri uri)     {         var content = base.LoadContent(uri);         if (content == null)             return null;         var vm = Caliburn.Micro.ViewModelLocator.LocateForView(content);         if (vm == null)             return content;         if (content is DependencyObject)         {             Caliburn.Micro.ViewModelBinder.Bind(vm, content as DependencyObject, null);         }         return content;     } } 
  6. ModernWindowView.xaml & ModernWindowViewModel.cs

     < mui:ModernWindow x:Class="MuiWithCaliburn01.ModernWindowView"     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"      xmlns:mui="http://firstfloorsoftware.com/ModernUI"     ContentLoader="{StaticResource ModernContentLoader}"> < mui:ModernWindow.MenuLinkGroups>     < mui:LinkGroupCollection>         < mui:LinkGroup DisplayName="Hello">             < mui:LinkGroup.Links>                 < mui:Link Source="Child1View.xaml" DisplayName="Click me">< /mui:Link>             < /mui:LinkGroup.Links>         < /mui:LinkGroup>     < /mui:LinkGroupCollection> < /mui:ModernWindow.MenuLinkGroups> < /mui:ModernWindow> 

     class ModernWindowViewModel : Conductor.Collection.OneActive {     public ModernWindowViewModel()     {         //this function is doing nothing in the ModernWindow, but it works great in the Window.         ActivateItem(new Child1ViewModel());     } } 

  7. And finally, the Child1View.xaml

    <UserControl x:Class="MuiWithCaliburn01.Child1View"     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"      xmlns:mui="http://firstfloorsoftware.com/ModernUI"     mc:Ignorable="d"      xmlns:cal="http://www.caliburnproject.org"     xmlns:model="clr-namespace:MuiWithCaliburn01"     d:DataContext="{x:Type model:Child1ViewModel}"     d:DesignHeight="300" d:DesignWidth="300">     <Grid>         <Button cal:Message.Attach="ClickMe" Width="140" Height="50">Hello World</Button>     </Grid> </UserControl> 

    and Child1ViewModel.cs

    public class Child1ViewModel : Conductor<IScreen> {     public void ClickMe()     {         MessageBox.Show("Hello");     } } 

My question is, step 6, why does the function do nothing? and if my way is wrong can anybody direct me to a better way?

And if it's the best approach, how can I navigate from the function ClickMe to another View.

1 Answers

Answers 1

As for your first question about why does the function do nothing, I think it may have to do with the hierarchy of your project. Using MUI this is the main (and only) window for one of my applications

<mui:ModernWindow x:Class="namespace.MainWindow"     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"     xmlns:mui="http://firstfloorsoftware.com/ModernUI"     Title="Add Game To State" IsTitleVisible="True"     ContentSource="/Pages/Home.xaml"     Style="{StaticResource MyModernWindow}">  <mui:ModernWindow.TitleLinks>     <mui:Link DisplayName="settings" Source="/Pages/SettingsPage.xaml" /> </mui:ModernWindow.TitleLinks> 

in my project hierarchy, i have my project root (CSPROJ file), MainWindow.xaml then a Pages folder. Inside my pages folder i have my SettingsPage.xaml. The source attribute in my mui:Link tag points to the realitive path of my main window to my settingsPage.xaml file. MUI will then load and display that path in the content provider that is put in your main window for you by the MUI ModernWindow class default style template. No additional code is needed on your part to navigate (until you want complex sub view navigation).

my settingsPage.xaml file is a normal user control with the grid having a style assigned to the ContentRoot static resource style as it also will contain additional views/pages.

Mui source can be found on GitHub at Mui GithubLink. Here you can download the sample program, who's code is found in the same repository under app Link here for covience.

I am not familiar with Caliburn.Micro so i'm not sure how the two integrate together, such as requirements of Caliburn.Micro to function. I do know how MUI integrates with MVVM light and there are many examples of this found on the internet in my research before hand.

Read More