hi I'm try to develop Datalogger, so i create a menu, to Switch de Options i decided to use DataTemplates and different ViewModels.
Menu
XAML:
<Window.Resources>
<DataTemplate x:Name="GraficoVtemplate" DataType="{x:Type viewmodels:GraficoVM}">
<view:GraficoV DataContext="{Binding}" />
</DataTemplate>
<DataTemplate x:Name="ListaVtemplate" DataType="{x:Type viewmodels:ListaVM}">
<view:ListaV DataContext="{Binding }"/>
</DataTemplate>
<ContentControl Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="4" Grid.RowSpan="5" Content="{Binding}"/>
This is how i change the datacontext;
XAML.CS
private void Novoteste_btn_Click(object sender, RoutedEventArgs e)
{
DataContext = new NovoTesteVM();
}
private void Lista_btn_Click(object sender, RoutedEventArgs e)
{
DataContext = new ListaVM();
}
This is the files that i have, the models are empty and the viewsmodals have some controles.Files
The problem is that when I change the menu, the content of the previous menu is not saved, that is when I select the menu "lista" and fill in a datagrid, and I go to another menu when I select again the menu "lista" the data are lost.I do not know what I need to add, or change so that the data is not lost
Thanks you for the explanation!
Edit 1:
MainWindows.xaml.cs
private void Novoteste_btn_Click(object sender, RoutedEventArgs e)
{
DataContext = NovoTesteVM.NovoTesteViewModel;
}
private void Grafico_btn_Click(object sender, RoutedEventArgs e)
{
DataContext = GraficoVM.Grafico;
}
NovoTesteVM.cs
public class NovoTesteVM
{
private static NovoTesteVM novoTesteViewModel;
public static NovoTesteVM NovoTesteViewModel
{
get
{
novoTesteViewModel = novoTesteViewModel ?? new NovoTesteVM();
return novoTesteViewModel;
}
}
}
Create container properties to keep the wiewmodel for corresponding views. It's loosing data because you are initializing new object on click.
Sample code -
private NovoTesteVM novoTesteViewModel;
public NovoTesteVM NovoTesteViewModel
{
get
{
novoTesteViewModel = novoTesteViewModel ?? new NovoTesteViewModel();
return novoTesteViewModel;
}
}
Related
I'm trying to do some basic UI and binding in UWP to get my head wrapped around it but I'm having trouble accessing a button within a listview item.
I have a Button where on clicking it, it creates a new object which is added to my ObservableCollection which then ends up adding a new item in my ListView
XMAL
<ListView Grid.Row="1" ItemsSource="{x:Bind counts}" x:Name="buttonsView" Margin="5,0" Background="White" Foreground="#FF5059AB">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Counter">
<StackPanel Orientation="Vertical">
<TextBlock Text="{x:Bind CountValue, Mode=TwoWay}" FontWeight="Black"/>
<Button Click="Increment_Click" Content="Increment"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C#
public class Counter
{
private int count;
public Counter()
{
count = 0;
}
public int CountValue
{
get
{
return count;
}
set
{
count = value;
}
}
}
public sealed partial class MainPage : Page
{
ObservableCollection<Counter> counts = new ObservableCollection<Counter>();
public MainPage()
{
this.InitializeComponent();
}
private void Increment_Click(object sender, RoutedEventArgs e)
{
// ?
}
private void AddCounter_Click(object sender, RoutedEventArgs e)
{
counts.Add(new Counter());
}
}
That works.
But when I click on the button within the StackPanel, the Increment_Click method is called but I'm not sure what to do at that point. I would like to access the Counter object by getting the index of the ListView item and using that to index into the ObservableCollection.
How do I figure out what the index is of the ListView item that was added?
Instead of an event you should use Command and CommandParameter. You would then bind the Command to a command implemented in Counter and CommandParameter to the item (like {Binding}).
However, you can achieve your goal with Click event as well using DataContext:
private void Increment_Click(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var counter = (Counter)button.DataContext;
//...
}
Basically DataContext is by default inherited from the parent unless you specify otherwise, so in this case DataContext is the current list item.
I have a MainWindow and 4 UserControls. By switching the DataContext to my UserControls I can have an application with multiple "Pages". In every UserControl I have a webBrowser-Control that Displays an PowerPoint (so -> 4 UC = 4 ppt). The issue I have now is that when I Switch my DataContext (Switch Page) I have to load (call the navigate Method) the whole ppt in my webBrowser again and that takes some Time. How can I fix this?
thanks in advance :))
Adrian
EDIT CODE
MainWindow.xaml
<Window.Resources>
<DataTemplate x:Name="Page1Template" DataType="{x:Type viewmodels:Page1Model}" >
<views:Page1 DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Page2Template" DataType="{x:Type viewmodels:Page2Model}">
<views:Page2 DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Page3Template" DataType="{x:Type viewmodels:Page3Model}">
<views:Page3 DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="Page4Template" DataType="{x:Type viewmodels:Page4Model}">
<views:Page4 DataContext="{Binding}"/>
</Window.Resources>
// ...
<ContentControl Content="{Binding}"></ContentControl>
MainWindow.xaml.cs (i call the page Switch like this)
private void menuBtn1_Click(object sender, RoutedEventArgs e)
{
DataContext = new Page1Model();
}
private void menuBtn2_Click(object sender, RoutedEventArgs e)
{
DataContext = new Page2Model();
}
private void menuBtn3_Click(object sender, RoutedEventArgs e)
{
DataContext = new Page3Model();
}
private void menuBtn4_Click(object sender, RoutedEventArgs e)
{
DataContext = new Page4Model();
}
and lets say e.g my UserControl1: (when i call UC1 every time the ppt is opening again, i want just to open it one time):
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
powerPointBrowser1.Navigate("somePPTfile.pptx");
powerPointBrowser1.LoadCompleted += powerPointBrowser1_LoadCompleted;
}
i hope i made it clear :S
In Mainwindow.xaml Place the view in stackpanel to make visiblity hide show like this.
<DataTemplate x:Name="Page1Template" DataType="{x:Type viewmodels:Page1Model}">
<StackPanel Visibility="{Binding Page1}">
<views:Page1 DataContext="{Binding}"/>
</StackPanel>
</DataTemplate>
In MainWindow.xaml.cs you have to set a property for visibility like this.
private Visibility page1;
public Visibility Page1
{
get { return page1; }
set { page1 = value; }
}
Then initialize the DataContext of each view in your MainWindowLoad function, so that it will be preloaded. After that you can set the visibility for the each view in your each menu click function like this:
Page1 = Visibility.Visible; or Page1 = Visibility.Collapsed;
I hope it will works.
I would like to have a TabControl with two TabItems and one Listbox on each Item. I want both ListBoxes to show the same content and so I bound both to the same ObservableCollection<T>. At first the items are shown correctly in Listbox1. Also if i switch to ListBox2 the Items show up here as well. If I go back to Listbox1 afterwards all the items are gone and stay in ListBox2. I want both ListBoxes to hold and show the same ListBoxItems constantly. Really would appreciate some help!
My XAML Code:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="350*"/>
<RowDefinition Height="100*"/>
</Grid.RowDefinitions>
<Button Grid.Row="1" Margin="5,5,5,5" Content="Add" Click="Button_Click"/>
<TabControl Grid.Row="0">
<TabItem Header="Test1">
<ListBox ItemsSource="{Binding Components}"/>
</TabItem>
<TabItem Header="Test2">
<ListBox ItemsSource="{Binding Components}"/>
</TabItem>
</TabControl>
</Grid>
CS Code:
private ListBoxItem _oBoxItem;
private Int32 i = 0;
private ObservableCollection<ListBoxItem> components = new ObservableCollection<ListBoxItem>();
public ObservableCollection<ListBoxItem> Components
{
get
{
if (components == null)
components = new ObservableCollection<ListBoxItem>();
return components;
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_oBoxItem = new ListBoxItem();
_oBoxItem.Content = "Part " + i.ToString();
Components.Add(_oBoxItem);
i += 1;
}
Don't add any visual elements such as ListBoxItem to the source collection. Add strings instead and you will get the expected results:
public partial class MainWindow : Window
{
private Int32 i = 0;
private ObservableCollection<string> components = new ObservableCollection<string>();
public ObservableCollection<string> Components
{
get
{
if (components == null)
components = new ObservableCollection<string>();
return components;
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Components.Add("Part " + i.ToString());
i += 1;
}
}
A visual element can only appear once in the visual tree. This basically means that the ListBoxItem that you add to the first ListBox cannot be displayed in the second ListBox and vice versa.
Below is an mcve (to reproduce the problem it has to be datatemplated unloadable view).
xaml:
<Window Content="{Binding}" ... >
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModel}">
<ListView ItemsSource="{Binding Items}"
SelectionChanged="ListView_SelectionChanged"
Unloaded="ListView_Unloaded"
MouseRightButtonDown="ListView_MouseRightButtonDown">
</ListView>
</DataTemplate>
</Window.Resources>
</Window>
cs:
public class ViewModel
{
public ObservableCollection<string> Items { get; set; } = new ObservableCollection<string>(new[] { "1", "2", "3" });
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e) => Title += "S";
void ListView_Unloaded(object sender, RoutedEventArgs e) => Title += "U";
void ListView_MouseRightButtonDown(object sender, MouseButtonEventArgs e) => DataContext = null;
}
Start program, select item in the list ("S" will be added to title), right click inside list -> "SU" will be added to title.
Question: why SelectionChanged is called when view (ListView) is unloaded??
Right-clicking without selecting anything will add only "U" to the tittle.
Simply closing software (disregarding selection) will not cause SelectionChanged (tested by setting breakpoint).
I will have some logic in SelectionChanged event and I don't want it to run when view is unloaded, it's unexpected behavior what SelectionChanged event is called in this case at all.
I am using Visual Studio 2015 with Update 1. I have created a new UWP Windows 10 application with 2 Blank Pages.
The first page has a GridView with an itemClick event. The object I am binding to the GridViewItem has a string field "Link" containing the name of the Page I will navigate to, when clicking on this GridViewItem.
private void GridView_ItemClick(object sender, ItemClickEventArgs e)
{
var link = (sender as Menu).Link;
Frame.Navigate(typeof(link));
}
But this is not possible... since "link" here is used like a type.
Is there a way to cast it, and make it work?
First of all, when you use the ItemClick event, the "sender" is not your Menu class, it is the GridView control itself. So your code var link = (sender as Menu).Link; should get a null reference exception.
Here I can provider two ways to do this work, but all of these two ways are using the SelectionChanged event like this:
private void gridView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var link = (gridView.SelectedItem as Menu).Link;
Frame.Navigate(link);
}
First one, define two properties in your Menu class like this:
public class Menu
{
public Type Link { get; set; }
public string Name { get; set; }
}
And use the GridView like this:
<GridView x:Name="gridView" ItemsSource="{x:Bind menu}" SelectionChanged="gridView_SelectionChanged">
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" FontSize="25" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
the code behind to add data to GridView:
private ObservableCollection<Menu> menu = new ObservableCollection<Menu>();
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
menu.Clear();
menu.Add(new Menu { Link = typeof(Link1), Name = typeof(Link1).Name });
menu.Add(new Menu { Link = typeof(Link2), Name = typeof(Link2).Name });
menu.Add(new Menu { Link = typeof(Link3), Name = typeof(Link3).Name });
}
Second one, you can just define one property in the Menu class, but use a Converter to display the name of each page.
Menu class:
public class Menu
{
public Type Link { get; set; }
}
the TypeToStringConverter converter:
public class TypeToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return "Error";
var link = (value as Menu).Link;
return link.Name;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
And you can use this converter and the GridView in XAML like this:
<Page.Resources>
<local:TypeToStringConverter x:Key="cvt" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<GridView x:Name="gridView" ItemsSource="{x:Bind menu}" SelectionChanged="gridView_SelectionChanged">
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource cvt} }" FontSize="25" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</Grid>
the code behind to add data to GridView:
private ObservableCollection<Menu> menu = new ObservableCollection<Menu>();
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
menu.Clear();
menu.Add(new Menu { Link = typeof(Link1) });
menu.Add(new Menu { Link = typeof(Link2) });
menu.Add(new Menu { Link = typeof(Link3) });
}