I am making an article reading app (similar to the Bing News app) and I'm using a FlipView to go between the articles. The FlipView has its ItemsSource databound to an ObservableCollection<T> that holds the content of the article.
I only want to keep 3 articles in the ObservableCollection<T> for memory and performance reasons, so I subscribe to the flipView_SelectionChanged event and remove the item at Length - 1 for going back (to the right) and item at 0 for going forward (to the left)
The problem that I'm having is that when I remove the item at 0 after flipping using the touch gesture, the animation plays a second time.
How do I prevent the transition animation from playing a second time?
Here is an example. Create a new Blank Store App, and add the following:
public sealed partial class MainPage : Page
{
public ObservableCollection<int> Items { get; set; }
private Random _random = new Random(123);
public MainPage()
{
this.InitializeComponent();
Items = new ObservableCollection<int>();
Items.Add(1);
Items.Add(1);
Items.Add(1);
flipview.ItemsSource = Items;
}
private void flipview_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.flipview.SelectedIndex == 0)
{
Items.Insert(0, 1);
Items.RemoveAt(Items.Count - 1);
}
else if (this.flipview.SelectedIndex == this.flipview.Items.Count - 1)
{
Items.Add(1);
Items.RemoveAt(0);
}
}
}
and this in the .xaml
<Page.Resources>
<DataTemplate x:Key="DataTemplate">
<Grid>
<Grid.Background>
<LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<FlipView x:Name="flipview"
SelectionChanged="flipview_SelectionChanged"
ItemsSource="{Binding}" ItemTemplate="{StaticResource DataTemplate}"/>
</Grid>
The easiest solution is to use UseTouchAnimationsForAllNavigation so that items manipulation in the view model does not cause the animation to occur at all.
<FlipView UseTouchAnimationsForAllNavigation="False" />
If the animation is important to you, then you can simply bind the value for UseTouchAnimationsForAllNavigation in your view model, like this:
<FlipView UseTouchAnimationsForAllNavigation="{Binding ShowAnimations}" />
Does this make sense? If you do it in your view model (option 2) you can simply disable it while manipulating the collection, and then re-enable it so you get animations.
Here's a sample.
Using this code:
public class MyColorModel : BindableBase
{
Color _Color = default(Color);
public Color Color { get { return _Color; } set { SetProperty(ref _Color, value); } }
Visibility _Selected = Visibility.Collapsed;
public Visibility Selected { get { return _Selected; } set { SetProperty(ref _Selected, value); } }
public event EventHandler RemoveRequested;
public void RemoveMe()
{
if (RemoveRequested != null)
RemoveRequested(this, EventArgs.Empty);
}
public event EventHandler SelectRequested;
public void SelectMe()
{
if (SelectRequested != null)
SelectRequested(this, EventArgs.Empty);
}
}
public class MyViewModel : BindableBase
{
public MyViewModel()
{
this.Selected = this.Colors[1];
foreach (var item in this.Colors)
{
item.RemoveRequested += (s, e) =>
{
this.ShowAnimations = false;
this.Colors.Remove(s as MyColorModel);
this.ShowAnimations = true;
};
item.SelectRequested += (s, e) => this.Selected = s as MyColorModel;
}
}
ObservableCollection<MyColorModel> _Colors = new ObservableCollection<MyColorModel>(new[]
{
new MyColorModel{ Color=Windows.UI.Colors.Red },
new MyColorModel{ Color=Windows.UI.Colors.Green },
new MyColorModel{ Color=Windows.UI.Colors.Yellow },
new MyColorModel{ Color=Windows.UI.Colors.Blue },
new MyColorModel{ Color=Windows.UI.Colors.White },
new MyColorModel{ Color=Windows.UI.Colors.Brown },
new MyColorModel{ Color=Windows.UI.Colors.SteelBlue },
new MyColorModel{ Color=Windows.UI.Colors.Goldenrod },
});
public ObservableCollection<MyColorModel> Colors { get { return _Colors; } }
MyColorModel _Selected = default(MyColorModel);
public MyColorModel Selected
{
get { return _Selected; }
set
{
if (_Selected != null)
_Selected.Selected = Visibility.Collapsed;
value.Selected = Visibility.Visible;
SetProperty(ref _Selected, value);
}
}
bool _ShowAnimations = true;
public bool ShowAnimations { get { return _ShowAnimations; } set { SetProperty(ref _ShowAnimations, value); } }
}
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (!object.Equals(storage, value))
{
storage = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Try this XAML:
<Page.DataContext>
<local:MyViewModel/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="150" />
</Grid.RowDefinitions>
<FlipView ItemsSource="{Binding Colors}" SelectedItem="{Binding Selected, Mode=TwoWay}" UseTouchAnimationsForAllNavigation="{Binding ShowAnimations}">
<FlipView.ItemTemplate>
<DataTemplate>
<Rectangle>
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Color}" />
</Rectangle.Fill>
</Rectangle>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
<ItemsControl ItemsSource="{Binding Colors}" Grid.Row="1" VerticalAlignment="Top" HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Grid>
<Rectangle Height="100" Width="100" Margin="10">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Color}" />
</Rectangle.Fill>
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="PointerPressed">
<Core:CallMethodAction MethodName="SelectMe" TargetObject="{Binding}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Rectangle>
<TextBlock Text="X" VerticalAlignment="Top" HorizontalAlignment="Right" FontSize="50" Margin="20,10" Foreground="Wheat">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="PointerPressed">
<Core:CallMethodAction MethodName="RemoveMe" TargetObject="{Binding}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</TextBlock>
</Grid>
<Rectangle Height="10" Width="100" Margin="10"
Fill="White" Visibility="{Binding Selected}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Will look like this:
Best of luck!
Related
All, I am having a lot of problems with displaying a custom tooltip for my LiveCharts control Here is my code and what I have tried.
Chart C# Code:
public partial class Chart : UserControl
{
public ChartData chartData { get; set; }
public Chart()
{
chartData = new ChartData();
InitializeComponent();
Loaded += Control_Loaded;
}
private void Control_Loaded(object sender, RoutedEventArgs e)
{
// build here
DataContext = this;
chartData.Formatter = value => value.ToString("C", CultureInfo.CurrentCulture);
}
}
Chart XAML:
<UserControl x:Class="LandlordTenantDatabaseWPF.Chart"
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:local="clr-namespace:LandlordTenantDatabaseWPF"
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance local:Chart}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock x:Name="txtTitle" Grid.Row="0" Text="{Binding chartData.Title}" Foreground="#FF4B52E4" FontSize="36" TextAlignment="Center"/>
<lvc:CartesianChart x:Name="chart" Grid.Row="1" Series="{Binding chartData.SeriesCollection}" LegendLocation="Right" >
<lvc:CartesianChart.AxisX>
<lvc:Axis Title="Month" Labels="{Binding chartData.Labels}"></lvc:Axis>
</lvc:CartesianChart.AxisX>
<lvc:CartesianChart.AxisY>
<lvc:Axis Title="Payments" LabelFormatter="{Binding chartData.Formatter}"></lvc:Axis>
</lvc:CartesianChart.AxisY>
<lvc:CartesianChart.DataTooltip>
<local:CustomersTooltip/>
</lvc:CartesianChart.DataTooltip>
</lvc:CartesianChart>
</Grid>
ToolTip C# Code:
public partial class CustomersTooltip : IChartTooltip
{
private TooltipData _data;
public CustomersTooltip()
{
InitializeComponent();
//LiveCharts will inject the tooltip data in the Data property
//your job is only to display this data as required
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public TooltipData Data
{
get { return _data; }
set
{
_data = value;
OnPropertyChanged("Data");
}
}
public TooltipSelectionMode? SelectionMode { get; set; }
protected virtual void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ToolTip XAML:
<UserControl x:Class="LandlordTenantDatabaseWPF.CustomersTooltip"
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:wpf="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
xmlns:local="clr-namespace:LandlordTenantDatabaseWPF"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
d:DataContext="{d:DesignInstance local:CustomersTooltip}"
Background="#E4555555" Padding="20 10" BorderThickness="2" BorderBrush="#555555">
<ItemsControl ItemsSource="{Binding Data.Points}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type wpf:DataPointViewModel}">
<Grid Margin="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="Title"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="LastName"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="Phone"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="PurchasedItems"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Stroke="{Binding Series.Stroke}" Fill="{Binding Series.Fill}"
Height="15" Width="15"></Rectangle>
<TextBlock Grid.Column="1" Text="{Binding ChartPoint.Instance.(local:CustomerVm.Name)}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White"/>
<TextBlock Grid.Column="2" Text="{Binding ChartPoint.Instance.(local:CustomerVm.LastName)}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White"/>
<TextBlock Grid.Column="3" Text="{Binding ChartPoint.Instance.(local:CustomerVm.Phone),
StringFormat=Phone: {0}}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White"/>
<TextBlock Grid.Column="4" Text="{Binding ChartPoint.Instance.(local:CustomerVm.PurchasedItems),
StringFormat=Purchased Items: {0:N}}"
Margin="5 0 0 0" VerticalAlignment="Center" Foreground="White"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ChartData Class:
public class ChartData : INotifyPropertyChanged
{
private SeriesCollection seriesCollection;
public SeriesCollection SeriesCollection
{
get { return seriesCollection; }
set
{
if (seriesCollection != value)
{
seriesCollection = value;
OnPropertyChanged("seriesCollection");
}
}
}
private string[] labels;
public string[] Labels
{
get { return labels; }
set
{
if(labels != value)
{
labels = value;
OnPropertyChanged("labels");
}
}
}
private string title;
public string Title
{
get { return title; }
set
{
if(title != value)
{
title = value;
OnPropertyChanged("title");
}
}
}
private Func<double, string> formatter;
public Func<double, string> Formatter
{
get { return formatter; }
set
{
if(formatter != value)
{
formatter = value;
OnPropertyChanged("formatter");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And the Window Containing the Chart:
private void AddNewSeries(List<double> lstPayments, string sSeriesName, SolidColorBrush colorBrush)
{
if (m_SeriesCollection == null)
{
m_SeriesCollection = new SeriesCollection
{ new ColumnSeries
{
Values = new ChartValues<double>(lstPayments),
Title = sSeriesName,
Fill = colorBrush
}
};
}
else
{
m_SeriesCollection.Add(
new ColumnSeries
{
Values = new ChartValues<double>(lstPayments),
Title = sSeriesName,
Fill = colorBrush
}
);
}
}
private void UpdateChartsBasedOnDates(Chart chart, string sChartTitle)
{ //Pass data to the chart
chart.chartData.SeriesCollection = m_SeriesCollection;
chart.chartData.Labels = GetFormattedDates(m_lstCombinedDates);
chart.chartData.Title = sChartTitle;
}
This all works perfectly when I just allow the default tooltip to be displayed on the chart. I am hung up on the fact that the chart data is supplied by a SeriesCollection, but the custom ToolTip, in the example on the LiveCharts website https://lvcharts.net/App/examples/v1/wpf/Tooltips%20and%20Legends doesn't use the SeriesCollection. Some of the charts displayed by my custom chart control have more than 1 series. I tried binding the Tooltip control to the SeriesCollection to get the data values, but that didn't work. Granted I may have done it incorrectly. Obviously the ToolTip XAML I am showing here is from the example code on the LiveCharts website, but I don't know where to go from here.
Is it possible to use make the tooltip use the series collection? What would be the best way for me to either use the Series Collection, or the ChartData class, or can I use
public TooltipData Data
{
get { return _data; }
set
{
_data = value;
OnPropertyChanged("Data");
}
}
from the ToolTip C# code to display the data in the ToolTip?
I'm so confused.
Edit: I forgot to mention what my goal is. I want to display a tooltip that says
string.Format("{0} Payment: ${1}", SeriesName, PaymentAmount);
This would display Rent Payment: $1000. or MortgagePayment: $1000.00
Without being a deep LiveCharts Tooltip Wiz I did manage to get a version going that worked for me. My chart is also using a SeriesCollection, so maybe this can be helpful for you.
This is how I integrated it into my chart:
CartesianChart cartesianChart = ...;
_cartesianChart.DataTooltip = new CustomTooltip
{
SelectionMode = TooltipSelectionMode.OnlySender
};
This is my CustomToolTip class (I believe this is pretty much straight out of the documentation somewhere):
public partial class CustomTooltip : UserControl, IChartTooltip
{
private TooltipData _data;
public CustomTooltip()
{
InitializeComponent();
//LiveCharts will inject the tooltip data in the Data property
//your job is only to display this data as required
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public TooltipData Data
{
get => _data;
set
{
_data = value;
OnPropertyChanged("Data");
}
}
public TooltipSelectionMode? SelectionMode { get; set; }
protected virtual void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class CustomTooltip : UserControl, IChartTooltip, INotifyPropertyChanged
{
private bool _contentLoaded;
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
public void InitializeComponent()
{
if (_contentLoaded)
return;
System.Uri resourceLocater = new System.Uri(
"/Plotting/CustomTooltipDesign.xaml",
System.UriKind.Relative
);
System.Windows.Application.LoadComponent(this, resourceLocater);
_contentLoaded = true;
}
}
Then I built a XAML component, just like you. First super simple:
public partial class CustomTooltipDesign : UserControl
{
public CustomTooltipDesign()
{
DataContext = this;
}
}
Then I had a little more effort to build the actual tooltip:
<UserControl x:Class="expomrf4utility.CustomTooltipDesign"
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:expomrf4utility="clr-namespace:ExpoMRF4Utility"
xmlns:wpf="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance expomrf4utility:CustomTooltip}"
x:Name="Control" d:DesignWidth="1" d:DesignHeight="1">
<UserControl.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{Binding Foreground}"></Setter>
</Style>
<wpf:SharedConverter x:Key="SharedConverter"/>
<wpf:SharedVisibilityConverter x:Key="SharedVisibilityConverter"/>
<wpf:ChartPointLabelConverter x:Key="ChartPointLabelConverter"/>
<wpf:ParticipationVisibilityConverter x:Key="ParticipationVisibilityConverter"/>
<BooleanToVisibilityConverter x:Key="Bvc"></BooleanToVisibilityConverter>
<CollectionViewSource x:Key="GroupedPoints" Source="{Binding Data.Points}"/>
</UserControl.Resources>
<UserControl.Template>
<ControlTemplate>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" >
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding Source={StaticResource GroupedPoints}}" Grid.IsSharedSizeScope="True">
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Name}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type wpf:DataPointViewModel}">
<Grid Margin="2" VerticalAlignment="Center" HorizontalAlignment="Center" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" SharedSizeGroup="Title"/>
<RowDefinition Height="Auto" SharedSizeGroup="Label"/>
<RowDefinition Height="Auto" SharedSizeGroup="Label"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="1" Text="{Binding Series.Title}" HorizontalAlignment="Left" FontWeight="Bold"/>
<TextBlock Grid.Row="2" Text="{Binding ChartPoint.Y, StringFormat='Value: {0} V/m'}" HorizontalAlignment="Left"/>
<TextBlock Grid.Row="3" Text="{Binding ChartPoint, Converter={StaticResource ChartPointLabelConverter}, StringFormat='Datetime: {0}'}" HorizontalAlignment="Left"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</ControlTemplate>
</UserControl.Template>
Note how I had to create bindings for the different properties that I wanted to display. To that end I slightly cheated when creating the data sets themselves. I used GLineSeries but I have a feeling it should work with other series as well:
int bandIndex = ...;
GearedValues<double> gearedValues = (some double[] data).AsGearedValues();
series.Add(new GLineSeries
{
Values = gearedValues,
LabelPoint = chartPoint => loggerData.Labels[(int)chartPoint.X].ToString(),
Tag = bandIndex,
Title = bandIndex.ToString()
});
Note that I chartPoint.X is the (zero-based) index of the the specific point (in X) - whereas chartPoint.Y is the Y-value of the point (also used in the XAML). Using this index I can look up in my list of names loggerData.Labels and return the timestamp associated with this index.
I have an ItemsControl with an ItemSource of Entities which is an ObservableCollection<Entity>.
The Entity class contains a string Name and int X,Y properties with public {get;set;}
<ItemsControl x:Name="myCanvas" Grid.Row="1" ItemsSource="{Binding Path=Entities}" MouseMove="myCanvas_MouseMove" MouseDoubleClick="myCanvas_MouseDoubleClick" MouseUp="myCanvas_MouseUp" MouseDown="myCanvas_MouseDown" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=Y,Mode=TwoWay}" />
<Setter Property="Canvas.Top" Value="{Binding Path=X,Mode=TwoWay}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle Width="50" Height="50" RadiusX="4" RadiusY="4" Stroke="Black" Fill="Red" />
<Label Content="{Binding Path=Name}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
When I add new Entity in my collection I can see the change in the UI normally. I want to move by mouse those rectangles, that is why I capture the mouse position in the release event of the ItemsControl and change the Entity positions in my collection Entities but I cannot see any changes in the UI.
MouseUp event
private void myCanvas_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Point point = Mouse.GetPosition(sender as FrameworkElement);
if (e.LeftButton == MouseButtonState.Released)
{
Entities[tempIndex].X = (int)point.X;
Entities[tempIndex].Y = (int)point.Y;
}
var binding = new Binding { Source = Entities };
myCanvas.SetBinding(ItemsControl.ItemsSourceProperty, binding);
}
tempIndex is the Entity that I want to move.
Your Canvas.Left is binding to Y (should be X).
Your Canvas.Top is binding to X (should be Y).
And Clemens is correct. Setting the binding for ItemsSource, in code, is not needed.
Here is some code that moves Entity #2 to the location you click on on myCanvas. I didn't implement the whole drag/drop, just moves the entity to where you click. I added a datagrid to the top to show the values of the Entities.
MainWindow.xaml
<Window x:Class="WpfApp11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp11"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Path=Entities}" AutoGenerateColumns="True" CanUserAddRows="False" />
<ItemsControl x:Name="myCanvas" Grid.Row="1" ItemsSource="{Binding Path=Entities}" MouseMove="myCanvas_MouseMove" MouseDoubleClick="myCanvas_MouseDoubleClick" MouseUp="myCanvas_MouseUp" MouseDown="myCanvas_MouseDown" Background="Beige" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=X,Mode=TwoWay}" />
<Setter Property="Canvas.Top" Value="{Binding Path=Y,Mode=TwoWay}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Rectangle Width="50" Height="50" RadiusX="4" RadiusY="4" Stroke="Black" Fill="Red" />
<Label Content="{Binding Path=Name}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
namespace WpfApp11
{
public partial class MainWindow : Window
{
public ObservableCollection<Entity> Entities { get; set; }
private int tempIndex = 1; // Always move box "2"
public MainWindow()
{
InitializeComponent();
DataContext = this;
Entities = new ObservableCollection<Entity>()
{
new Entity() { Name = "1", X=50, Y=50 },
new Entity() { Name = "2", X=150, Y=50 },
new Entity() { Name = "3", X=50, Y=150 },
new Entity() { Name = "4", X=150, Y=150 },
};
}
private void myCanvas_MouseMove(object sender, MouseEventArgs e)
{
}
private void myCanvas_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
}
private void myCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
}
private void myCanvas_MouseUp(object sender, MouseButtonEventArgs e)
{
Point point = Mouse.GetPosition(sender as FrameworkElement);
if (e.LeftButton == MouseButtonState.Released)
{
Entities[tempIndex].X = (int)point.X;
Entities[tempIndex].Y = (int)point.Y;
}
}
}
public class Entity : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName]string name = null)
{
if (Equals(field, value))
{
return false;
}
field = value;
this.OnPropertyChanged(name);
return true;
}
protected void OnPropertyChanged([CallerMemberName]string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
#endregion
#region Property string Name
private string _Name;
public string Name { get { return _Name; } set { SetProperty(ref _Name, value); } }
#endregion
#region Property int X
private int _X;
public int X { get { return _X; } set { SetProperty(ref _X, value); } }
#endregion
#region Property int Y
private int _Y;
public int Y { get { return _Y; } set { SetProperty(ref _Y, value); } }
#endregion
}
}
So i've been trying to build a simple rss feed app for win10 using universal apps and mvvmcross that will do the following:
Load all items from an address in a listbox or something similar which will show them in the splitview.
When clicking on a title it will open a WebView with the full article of the specific title.
The problem I have, is binding different parts of the same RssItems to different controls but keep their relation. I'm new to both technologies but i think it should be possible somehow, I just can't find the way.
These are the related parts of the code I wrote:
viewModel.cs:
class FirstViewModel : MvxViewModel
{
private List<string> _rssItems;
public List<string> RssItems
{
get { return _rssItems; }
set { _rssItems = value; RaisePropertyChanged(() => RssItems); }
}
public MvxCommand SelectionChangedCommand
{
get
{
return new MvxCommand(() =>
{
LoadRssItems();
});
}
}
private async void LoadRssItems()
{
List<string> feedItems = new List<string>();
SyndicationClient rssReaderClient = new SyndicationClient();
SyndicationFeed rssFeed = await rssReaderClient.RetrieveFeedAsync(new Uri("xml address"));
if (rssFeed != null)
{
foreach (var item in rssFeed.Items)
{
feedItems.Add(item.Title.Text);
RssItemsOrinigal.Add(item);
}
RssItems = feedItems;
}
}
And the firstView.xaml:
<views:MvxWindowsPage
x:Class="RssReader.Views.FirstView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:RssReader.Views"
xmlns:views="using:MvvmCross.WindowsUWP.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<RelativePanel>
<Button x:Name="HamburgerButton"
RelativePanel.AlignLeftWithPanel="True"
FontFamily="Segoe MDL2 Assets"
FontSize="36"
Content=""
Click="HamburgerButton_Click"/>
<SplitView Grid.Row="1"
x:Name="sv"
DisplayMode="CompactOverlay"
OpenPaneLength="200"
CompactPaneLength="50">
<SplitView.Pane>
<ListBox x:Name="lstMenuItems" SelectionMode="Single">
<ListBoxItem x:Name="ListBoxItem1">
<StackPanel Orientation="Horizontal">
<Button FontFamily="Segoe MDL2 Assets" FontSize="36" Command="{Binding SelectionChangedCommand}"></Button>
<TextBlock FontSize="24" Text="Website 1" />
</StackPanel>
</ListBoxItem>
</ListBox>
</SplitView.Pane>
<SplitView.Content>
<ListBox ItemsSource="{Binding RssItems}"/>
</SplitView.Content>
</SplitView>
</Grid>
</views:MvxWindowsPage>
One way to do this is using ContentPresenter together with your ListBox and set the Visibility property each time when you select an item or want to go back to the ListBox:
<Page.Resources>
<DataTemplate x:Key="DetailContent">
<WebView Source="{Binding DetailUri}" />
</DataTemplate>
<DataTemplate x:Key="ListBoxContent">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</Page.Resources>
...
<SplitView.Content>
<Grid>
<ContentPresenter Content="{x:Bind listBox.SelectedItem, Mode=OneWay}"
ContentTemplate="{StaticResource DetailContent}" Visibility="{x:Bind VM.IsVisible, Mode=OneWay}" />
<ListBox x:Name="listBox" ItemsSource="{x:Bind VM.RssItems}"
ItemTemplate="{StaticResource ListBoxContent}" SelectionChanged="{x:Bind VM.listBox_SelectionChanged}" />
<Button Content="Close and Show ListBox" VerticalAlignment="Bottom" Visibility="{x:Bind VM.IsVisible, Mode=OneWay}"
Click="{x:Bind VM.IsVisible_Clicked}" />
</Grid>
</SplitView.Content>
In the ViewModel of this page for example:
public class Page6ViewModel : INotifyPropertyChanged
{
public ObservableCollection<ListBoxDetail> RssItems;
private Visibility _IsVisible = Visibility.Collapsed;
public Visibility IsVisible
{
get { return _IsVisible; }
set
{
if (value != _IsVisible)
{
_IsVisible = value;
OnpropertyChanged();
}
}
}
public Page6ViewModel()
{
RssItems = new ObservableCollection<ListBoxDetail>();
RssItems.Add(new ListBoxDetail { Title = "Item 1", DetailUri = "http://stackoverflow.com/questions/38853708/using-different-parts-of-rss-feed-in-a-universal-app-using-mvvmcross-and-command?noredirect=1#comment65137456_38853708" });
RssItems.Add(new ListBoxDetail { Title = "Item 2", DetailUri = "https://msdn.microsoft.com/en-us/library/windows/apps/ms668604(v=vs.105).aspx" });
RssItems.Add(new ListBoxDetail { Title = "Item 3", DetailUri = "https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.controls.itemscontrol.itemssource.aspx" });
}
private ListBox listBox;
public void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
listBox = sender as ListBox;
listBox.Visibility = Visibility.Collapsed;
IsVisible = Visibility.Visible;
}
public void IsVisible_Clicked(object sender, RoutedEventArgs e)
{
listBox.Visibility = Visibility.Visible;
IsVisible = Visibility.Collapsed;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnpropertyChanged([CallerMemberName]string propertyName = "")
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Another common method is using Frame as Content of SpiltView, then create a page for ListBox and a page for WebView, at first navigate this frame to the page of ListBox, in the SelectionChanged event, you can send the uri as parameter and navigate this frame to the page of WebView. For this method, there are many samples on the internet and you can search for them.
i am trying to bind the background color with the item template containing a grid in it. I want that depending upon the status the background color of each item in a list should be highlighted automatically. So i am doing it but no color is shown on the respective item.so how to bind the grid background such that color is changed. Xaml code :
<phone:LongListSelector x:Name="ResultListBox"
Margin="35,10,35,-25"
ItemsSource="{Binding Country}"
ItemTemplate="{StaticResource CustomList}"
Height="436" SelectionChanged="ResultListBox_SelectionChanged" Loaded="Listbox_loaded">
<UserControl.Resources>
<DataTemplate x:Key="CustomList">
<Grid Margin="5,0,10,5" Tag="{Binding Name}" x:Name="CountryGrid" Tap="BorderColor" >
<Grid.Background>
<SolidColorBrush Color="{Binding HighlightBackgroundColor}" />
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="450"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="45"></RowDefinition>
</Grid.RowDefinitions>
<Border VerticalAlignment="Center" x:Name="HighlightBG" HorizontalAlignment="Left" Grid.Column="0" Margin="0,5,0,0" Height="70" CornerRadius="0,10,10,0" Grid.Row="0" >
<StackPanel Orientation="Vertical" Margin="0,5,0,0" HorizontalAlignment="Center" >
<TextBlock Text="{Binding Name}" x:Name="nametextblock" VerticalAlignment="Top" TextWrapping="Wrap" Foreground="White" FontSize="30" HorizontalAlignment="Center" ></TextBlock>
</StackPanel>
</Border>
</Grid>
</DataTemplate>
</UserControl.Resources>
c# code snippet:
public partial class ListPopup : UserControl,INotifyPropertyChanged
{
public ListPopup()
{
InitializeComponent();
this.Loaded += ListPopup_Loaded;
this.IsSelected = false;
//, INotifyPropertyChanged
this.NonHighlightColor = new SolidColorBrush(Colors.Transparent);
this.HighLightColor = new SolidColorBrush(Colors.Red);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
////////////
private bool _is_selected;
public bool IsSelected
{
get { return _is_selected; }
set
{
_is_selected = value;
OnPropertyChanged("HighlightBackgroundColor");
}
}
public SolidColorBrush HighlightBackgroundColor
{
get { if (IsSelected) return HighLightColor; else return NonHighlightColor; }
}
private SolidColorBrush HighLightColor { get; set; }
private SolidColorBrush NonHighlightColor { get; set; }
}
You're exposing a Brush but binding it as a Color. Just change your XAML:
<Grid Margin="5,0,10,5" Background="{Binding HighlightBackgroundColor}" Tag="{Binding Name}" x:Name="CountryGrid" Tap="BorderColor" >
I have a wpf with multiple tabs. The application is a step by step learning tool for students. I want the tabs to be locked initially. When the user has entered the correct data in a tab, the next is enabled after pressing the proceed button.
I tried binding the 'isEnabled' property of tabitem and setting it true when the button is pressed in the View model. It doesn't work.
Here's some snippets of my code:
XAML:
<TabItem Header="Step 1"><my:Step1 Loaded="Step1_Loaded" /></TabItem>
<TabItem Header="Step 2"><my:Step2 Loaded="Step2_Loaded" /></TabItem>
<TabItem IsEnabled="{Binding step3Enabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Step 3"><my:Step3 Loaded="Step3_Loaded" /></TabItem>
Code-behind for the button:
private void proceed2_Click(object sender, RoutedEventArgs e)
{
var vm = DataContext as ViewModel.ValuesCheck;
if (vm != null)
vm.Step2check();
}
View Model:
class ViewModel:INotifyPropertyChanged
{
bool _step3enabled = false;
public bool step3Enabled
{
get { return _step3enabled; }
set { _step3enabled = value; OnPropertyChanged("step3Enabled"); }
}
public void Step2check()
{
this.step3Enabled = true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
}
As I can understand, you need some navigation control that presents its content based on some condition. I can suggest you to use a listbox control and manage its ListBoxItems content visibility or something like this.
Xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding Controls}" SelectedItem="{Binding CurrentControlContent}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Visibility="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource Converter}}"
BorderBrush="Tomato" BorderThickness="1" IsEnabled="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<Button Width="100" Height="30" IsEnabled="{Binding Path = TabIsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
IsHitTestVisible="False" Content="{Binding Path = Content, UpdateSourceTrigger=PropertyChanged}"></Button>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ContentControl Content="{Binding CurrentControlContent.Content}"></ContentControl>
<Button Grid.Row="1" Content="Check" Command="{Binding CheckCommand}"></Button>
</Grid>
ViewModel
private TabAndControlModel _selectedTabControlModel;
private ICommand _checkCommand;
private TabAndControlModel _currentControlContent;
public ObservableCollection<TabAndControlModel> Controls { get; set; }
public TabAndControlModel CurrentControlContent
{
get { return _currentControlContent; }
set
{
_currentControlContent = value;
OnPropertyChanged();
}
}
public ICommand CheckCommand
{
get { return _checkCommand ?? (_checkCommand = new RelayCommand(Check)); }
}
private void Check()
{
var index = Controls.IndexOf(CurrentControlContent);
var nextIndex = index + 1;
if(nextIndex >= Controls.Count) return;
CurrentControlContent = Controls[nextIndex];
CurrentControlContent.TabIsEnabled = true;
}
Model
private bool _tabIsEnabled;
private string _content;
public bool TabIsEnabled
{
get { return _tabIsEnabled; }
set
{
_tabIsEnabled = value;
OnPropertyChanged();
}
}
public string Content
{
get { return _content; }
set
{
_content = value;
OnPropertyChanged();
}
}
View on running:
And you have to template and style all that stuff...
Regards