So I just started working with a library called LiveCharts and I am experimenting with one of the example projects and I've run into a issue.
I'm updating the source property LastHourSeries but the UI is not updating when I click the button which fires the UpdateChart function.
My MainWindow.xaml looks like this
<Window.DataContext>
<local:ChartControlViewModel/>
</Window.DataContext>
<Grid>
<local:ChartControl x:Name="Eh"/>
<Button Width="100" Height="25"
Content="Update"
Command="{Binding UpdateCommand}"
Margin="484,244,208,150"/>
</Grid>
And the userControl itself looks like this
<UserControl.DataContext>
<liveChartExample:ChartControlViewModel/>
</UserControl.DataContext>
<Grid Height="500" Width="650" >
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Margin="15, -60, 15, 15" MaxHeight="350">
<Grid.Effect>
<DropShadowEffect BlurRadius="15" Direction="-90" RenderingBias="Quality" Opacity=".2" ShadowDepth="1"/>
</Grid.Effect>
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=Border1}" />
</Grid.OpacityMask>
<Grid.Resources>
<Style TargetType="lvc:LineSeries">
<Setter Property="StrokeThickness" Value="3"></Setter>
<Setter Property="Stroke" Value="White"></Setter>
<Setter Property="Fill" Value="#4EFFFFFF"></Setter>
<Setter Property="PointGeometrySize" Value="0"></Setter>
<Setter Property="LineSmoothness" Value="0"></Setter>
</Style>
<Style TargetType="lvc:Axis">
<Setter Property="ShowLabels" Value="False"></Setter>
<Setter Property="IsEnabled" Value="False"></Setter>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height=".50*"></RowDefinition>
<RowDefinition Height=".5*"></RowDefinition>
</Grid.RowDefinitions>
<Border x:Name="Border1" Grid.Row="0" Grid.RowSpan="4" CornerRadius="5" Background="White" />
<Border Grid.Row="0" Grid.RowSpan="3" Background="#A61EE4" ></Border>
<TextBlock Grid.Row="0" TextAlignment="Center" Padding="10, 10, 0, 5" Foreground="White" FontSize="18">
The Current Chart
</TextBlock>
<TextBlock Grid.Row="1" TextAlignment="Center" Foreground="#59FFFFFF" Padding="0,0,0,20">2019.01.13</TextBlock>
<lvc:CartesianChart Grid.Row="2" Margin="0, 0, 0, 0" Series="{Binding LastHourSeries, UpdateSourceTrigger=PropertyChanged}" Hoverable="False" DataTooltip="{x:Null}">
<lvc:CartesianChart.AxisX>
<!--a small visual improvement, lets hide the first points (x = 0, x=1) to get better animations-->
<lvc:Axis MinValue="2"></lvc:Axis>
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
<StackPanel Grid.Row="3" VerticalAlignment="Center" Margin="25, 0">
<TextBlock Opacity=".4" FontSize="13">Total electricity Consumption <LineBreak /> of Galaxy SOHO</TextBlock>
<StackPanel Orientation="Horizontal">
<TextBlock Foreground="#303030" FontSize="40" Text="{Binding LastLecture, StringFormat={}{0:N1}}" />
<TextBlock Foreground="#303030" FontSize="18" VerticalAlignment="Bottom" Margin="8, 6">kWh</TextBlock>
</StackPanel>
</StackPanel>
</Grid>
</Grid>
And ofcourse the ViewModel
class ChartControlViewModel : ObservableObject
{
private SeriesCollection _lasthourSeriesCollection;
public SeriesCollection LastHourSeries
{
get { return _lasthourSeriesCollection; }
set
{
_lasthourSeriesCollection = value;
OnPropertyChanged();
}
}
public RelayCommand UpdateCommand { get; set; }
public ChartControlViewModel()
{
UpdateCommand = new RelayCommand(o => { UpdateChart(o); }, o => true);
LastHourSeries = new SeriesCollection
{
new LineSeries
{
AreaLimit = -10,
Values = new ChartValues<ObservableValue>
{
new ObservableValue(3),
new ObservableValue(5),
new ObservableValue(6),
new ObservableValue(7),
new ObservableValue(3),
new ObservableValue(4),
new ObservableValue(2),
new ObservableValue(5),
new ObservableValue(8),
new ObservableValue(3),
new ObservableValue(5),
new ObservableValue(6),
new ObservableValue(7),
new ObservableValue(3),
new ObservableValue(4),
new ObservableValue(2),
new ObservableValue(5),
new ObservableValue(8)
}
}
};
}
public void UpdateChart(object o)
{
LastHourSeries[0].Values.Add(new ObservableValue(100));
}
}
Is this problem strictly related to the library or is it me who is using databinding in a bad way?
Original project
https://lvcharts.net/App/examples/v1/wpf/Material%20Design
You are creating two instances of the ChartControlViewModel class. The UserControl should inherit the window's DataContext and not create its own view model. Try to remove this part from your UserControl's XAML:
<UserControl.DataContext>
<liveChartExample:ChartControlViewModel/>
</UserControl.DataContext>
Related
i am trying to create an application that will display user controls, based on information collected from DB. The problem is that if items total height exceeds mainwindow grid reserved for this display, i do not see a vertical scrollbar. From what i had found there might be an issue, that stackpanel/scrollviewer does not limit size of it's children. Im stuck on how to solve this, is there any way to stack items in limited spaces with scrollbar visible only when needed.
I want to avoid "hardcoding" the height of scrollviewer.
For now i'm stuck with below code:
XAML: (main window)
<Grid Name="selectedOptionGrid"
Grid.Column="1"
Grid.Row="1"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<StackPanel Name="selectedOptionStack"/>
<!-- Dock selected windows based on option selected-->
</Grid>
(user control, additional window docked to mainwindow)
<UserControl x:Class="Power_Planner_1._0.Team_Planner.MyTasks.ucTaskWindow"
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:Power_Planner_1._0.Team_Planner.MyTasks"
mc:Ignorable="d">
<Grid Background="White"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0"
Orientation="Horizontal"
VerticalAlignment="Top"
Margin="0,5,0,0">
<TextBlock Margin="60,0,0,0" Width="160" FontWeight="Bold">Task Name</TextBlock>
<TextBlock Margin="10,0,0,0" Width="160" FontWeight="Bold">Team Name</TextBlock>
<TextBlock Margin="0,0,0,0" Width="55" FontWeight="Bold">Deadline</TextBlock>
<TextBlock Margin="20,0,0,0" Width="60" FontWeight="Bold">Start</TextBlock>
<TextBlock Margin="28,0,0,0" Width="60" FontWeight="Bold">End</TextBlock>
<TextBlock Margin="20,0,0,0" Width="60" FontWeight="Bold">Run Time</TextBlock>
</StackPanel>
<Separator Grid.Row="1"
VerticalAlignment="Top"/>
<Grid Name="taskListGrid"
Grid.Row="2"
Height="510"
Width="830">
<ScrollViewer VerticalScrollBarVisibility="Auto"
Width="{Binding ActualWidth,RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=Grid}}"
Height="{Binding ActualHeight,RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=Grid}}">
<StackPanel Orientation="Vertical"
x:Name="taskListStack">
<StackPanel.Resources>
<Style TargetType="UserControl">
<Setter Property="Margin" Value="0,5,0,0"/>
</Style>
</StackPanel.Resources>
</StackPanel>
</ScrollViewer>
</Grid>
</Grid>
</UserControl>
And finally an Items that are created:
<UserControl x:Class="Power_Planner_1._0.Team_Planner.MyTasks.ucTaskItem"
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:Power_Planner_1._0.Team_Planner.MyTasks"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d" d:DesignHeight="45">
<Border CornerRadius="12,12,12,12"
BorderThickness="1,1,1,1"
BorderBrush="LightGray"
Background="#FF456780"
Padding="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="65"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="65"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!--Picture-->
<Border Grid.Column="0"
CornerRadius="25,25,25,25"
BorderThickness="1,1,1,1"
Width="30"
Height="30"
BorderBrush="LightGray"
Background="ForestGreen"
HorizontalAlignment="Left"
Margin="10,0,0,0">
</Border>
<!--Task Name-->
<TextBlock Grid.Column="1"
Margin="10,0,0,0"
FontSize="12"
Foreground="LightGray"
Text="{Binding taskName}"
Width="160"
VerticalAlignment="Center">
</TextBlock>
<!--Team Name-->
<TextBlock Grid.Column="2"
Margin="20,0,0,0"
FontSize="12"
Foreground="LightGray"
Text="{Binding teamName}"
Width="160"
VerticalAlignment="Center">
</TextBlock>
<!--Deadline-->
<TextBlock Grid.Column="3"
Margin="20,0,0,0"
FontSize="12"
Foreground="LightGray"
Text="{Binding deadline}"
Width="45"
VerticalAlignment="Center">
</TextBlock>
<!--Start-->
<Button Grid.Column="4"
Background="#FF456780"
BorderBrush="LightGray"
Height="25"
Margin="20,0,0,0"
Width="60">
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</Button.Resources>
<TextBlock Foreground="LightGray">Start</TextBlock>
</Button>
<!--Stop-->
<Button Grid.Column="5"
Background="#FF456780"
BorderBrush="LightGray"
Height="25"
Margin="20,0,0,0"
Width="60">
<Button.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</Button.Resources>
<TextBlock Foreground="LightGray">Stop</TextBlock>
</Button>
<!--Deadline-->
<TextBlock Grid.Column="6"
Margin="20,0,0,0"
FontSize="12"
Foreground="LightGray"
Text="{Binding runtime}"
Width="45"
VerticalAlignment="Center">
</TextBlock>
<!--Other Options-->
<materialDesign:PopupBox Grid.Column="7"
StaysOpen="False"
Foreground="White"
HorizontalAlignment="Right">
<StackPanel Width="150"
Background="White">
<Button Content="Edit"/>
<Button Content="View Details"/>
<Button Content="Comment"/>
</StackPanel>
</materialDesign:PopupBox>
</Grid>
</Border>
</UserControl>
Code to add items:
taskListStack.Children.Clear();
var task1 = new clsTaskItem("Test1", "blablabla", "12:40", "02:30");
taskListStack.Children.Add(new ucTaskItem(task1));
var task2 = new clsTaskItem("Test2", "blablabla", "13:45");
taskListStack.Children.Add(new ucTaskItem(task2));
var task3 = new clsTaskItem("Test3", "blablabla", "11:45");
taskListStack.Children.Add(new ucTaskItem(task3));
var task4 = new clsTaskItem("Test4", "blablabla", "14:45");
taskListStack.Children.Add(new ucTaskItem(task4));
var task5 = new clsTaskItem("Test5", "blablabla", "17:45");
taskListStack.Children.Add(new ucTaskItem(task5));
var task6 = new clsTaskItem("Test6", "blablabla", "18:45");
taskListStack.Children.Add(new ucTaskItem(task6));
var task7 = new clsTaskItem("Test7", "blablabla", "13:23");
taskListStack.Children.Add(new ucTaskItem(task7));
var task8 = new clsTaskItem("Test8", "blablabla", "12:54");
taskListStack.Children.Add(new ucTaskItem(task8));
var task9 = new clsTaskItem("Test9", "blablabla", "17:23");
taskListStack.Children.Add(new ucTaskItem(task9));
var task10 = new clsTaskItem("Test10", "blablabla", "17:10");
taskListStack.Children.Add(new ucTaskItem(task10));
taskListStack.Children.Add(new ucTaskItem(task1));
taskListStack.Children.Add(new ucTaskItem(task2));
taskListStack.Children.Add(new ucTaskItem(task3));
taskListStack.Children.Add(new ucTaskItem(task4));
taskListStack.Children.Add(new ucTaskItem(task5));
taskListStack.Children.Add(new ucTaskItem(task6));
taskListStack.Children.Add(new ucTaskItem(task7));
taskListStack.Children.Add(new ucTaskItem(task8));
taskListStack.Children.Add(new ucTaskItem(task9));
taskListStack.Children.Add(new ucTaskItem(task10));
And task item class ( to which items are binded via datacontext)
public class clsTaskItem
{
public clsTaskItem(string tsname, string tmname, string dline, string rtime = "00:00:00")
{
taskName = tsname;
teamName = tmname;
deadline = dline;
runtime = rtime;
}
public string taskName { get; private set; }
public string teamName { get; private set; }
public string deadline { get; private set; }
public string runtime { get; private set; }
}
As you can see i had tried to bind the scrollviewer height to grid i had created on top of it, thinking that if i set height to auto, it will get the height from the grid actual height. Well that didn't make much sense hence it was not working. I'm trying to find another solution for grouping the items, that way that i could expand easily main window, without coding each window to expand with this expansion so that items would both fill all available space and display scrollbar when needed.
Please help :)
I'm using LiveCharts in WPF to visualize the results of some analyses. The results of an analysis is added to a SeriesCollection and displayed in an CartesianChart. You can choose which type of series to use: LineSeries or ColumnSeries. The chosen type is then created and added to the SeriesCollection.
There's a custom mapper for selecting X and Y values from the ChartValues and a AxisFormatter for the X axis.
The charts are part of an Blacklight.Controls.Wpf.DragDockPanelHost. Each chart is an DragDockPanel with a style attached to it. The chart itself is a ContentControl with an TemplateSelector that returns the CartesianChart-XAML as a DataTemplate.
I've already tried to set the Fill or Stroke of the series or putting some ColumnSeries in there manually but that didn't help at all.
Filling of the SeriesCollection:
private SeriesCollection _Series;
public SeriesCollection Series
{
get { return _Series; }
set { SetProperty<SeriesCollection>(ref _Series, value); }
}
...
private void createDiagram()
{
if (this._Analysis!= null && this._Diagram != null)
{
this.Series.Clear();
foreach (KeyValuePair<state, Dictionary<DateTime, int>> kvp in this.Analysis.Execute())
{
Series series = Activator.CreateInstance(Diagram) as Series;
if (series != null)
{
series.Title = kvp.Key.name;
series.Values = new ChartValues<KeyValuePair<DateTime, int>>(kvp.Value);
this.Serien.Add(series);
}
}
}
}
Mapper and AxisFormatter:
CartesianMapper<KeyValuePair<DateTime, int>> mapper = Mappers.Xy<KeyValuePair<DateTime, int>>().X(kvp => ((DateTimeOffset)kvp.Key).ToUnixTimeSeconds()).Y(kvp => kvp.Value);
this.Series = new SeriesCollection(mapper);
this.XFormatter = value =>
{
return DateTimeOffset.FromUnixTimeSeconds((long)value).DateTime.ToString("dd.MM.yyyy HH:mm");
};
TemplateSelector:
public class DashboardElementTemplateSelector : DataTemplateSelector
{
public DataTemplate ListDashboardElementTemplate { get; set; }
public DataTemplate SingleValueDashboardElementTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is ListDashboardElementViewModel)
return this.ListDashboardElementTemplate;
else
return this.SingleValueDashboardElementTemplate;
}
}
XAML of DragDockPanelHost:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<DataTemplate x:Key="listElement">
<views:ListDashboardElementView/>
</DataTemplate>
<DataTemplate x:Key="singleValueElement">
<views:SingleValueDashboardElementView/>
</DataTemplate>
<tempselect:DashboardElementTemplateSelector x:Key="elementTempSelector"
ListDashboardElementTemplate="{StaticResource listElement}"
SingleValueDashboardElementTemplate="{StaticResource singleValueElement}"
/>
</ResourceDictionary>
<ResourceDictionary>
<conv:BooleanToVisibilityConverter x:Key="visCon"/>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<bl:DragDockPanelHost ItemsSource="{Binding Diagrams}" Grid.Row="1" Margin="20" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<bl:DragDockPanelHost.Style>
<Style TargetType="bl:DragDockPanelHost">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas ClipToBounds="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
</Canvas>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="bl:DragDockPanelHost">
<ItemsPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</bl:DragDockPanelHost.Style>
<bl:DragDockPanelHost.DefaultPanelStyle>
<Style TargetType="{x:Type bl:DragDockPanel}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Margin="10">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Background="#00000000" Margin="-2" Padding="5" Grid.Row="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<WrapPanel>
<Image Width="20" x:Name="GripBarElement" Source="/Aisys.XStorage.Dashboard;component/Images/move.png" Grid.Column="0" Cursor="Hand" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding Name}" Grid.Column="0" FontSize="16" FontWeight="Bold" Margin="10,0,0,0"/>
</WrapPanel>
<WrapPanel HorizontalAlignment="Right" Grid.Column="2">
<Button Command="{Binding ExecuteCommand}" CommandParameter="{Binding}" Margin="5,0,5,0">
<Button.Template>
<ControlTemplate>
<Image Source="/Aisys.XStorage.Dashboard;component/Images/Refresh.png"/>
</ControlTemplate>
</Button.Template>
</Button>
<Button Width="20" Command="{Binding DataContext.RemoveDiagramCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type bl:DragDockPanelHost}}}" CommandParameter="{Binding}">
<Button.Template>
<ControlTemplate>
<Image Source="/Aisys.XStorage.Dashboard;component/Images/Remove.png"/>
</ControlTemplate>
</Button.Template>
</Button>
<Button Command="{Binding DataContext.ShowPropertiesCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type bl:DragDockPanelHost}}}" CommandParameter="{Binding}" Margin="5,0,5,0">
<Button.Template>
<ControlTemplate>
<Image Source="/Aisys.XStorage.Dashboard;component/Images/Preferences.png"/>
</ControlTemplate>
</Button.Template>
</Button>
<ToggleButton x:Name="MaximizeToggleButton" VerticalAlignment="Top" HorizontalAlignment="Right" IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, Path=IsMaximized}" Margin="0,5,5,0" Width="25" Height="25" Cursor="Hand">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Image Source="/Aisys.XStorage.Dashboard;component/Images/Maximize.png" Margin="0,0,0,5"/>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</WrapPanel>
</Grid>
</Border>
<Separator VerticalAlignment="Bottom" Margin="0,0,0,0"/>
<ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource elementTempSelector}" Grid.Row="1" Margin="10"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</bl:DragDockPanelHost.DefaultPanelStyle>
</bl:DragDockPanelHost>
XAML of chart:
<Grid>
<lvc:CartesianChart Series="{Binding Series}" LegendLocation="Right" Name="chart">
<lvc:CartesianChart.AxisX>
<lvc:Axis Title="Zeit" LabelFormatter="{Binding XFormatter}">
</lvc:Axis>
</lvc:CartesianChart.AxisX>
</lvc:CartesianChart>
</Grid>
If I'm choosing LineSeries, everything works fine. But when I'm using ColumnSeries nothing is shown. You can see, that the axis is redrawn and the separators move. The legend is also drawn, but there are no columns visible.
Any ideas why this is happening?
I had the same problem recently. Unfortunately, this doesn't seem to be documented anywhere but for some reason if you have too many data points for the size of the graph, then none of the columns will display. You can either try reducing the number of data points until it works (in my case 90 data points wouldn't display, but 30 would), or on ColumnSeries there is a property ColumnPadding that you can turn down to zero and see if that helps.
In my Prism 6 WPF MVVM application I use custom modal dialog for login of the users. This dialog produces (in XAML) the following design-time error: 'No parameterless constructor defined for this object'. Below is XAML markup of the dialog:
<UserControl x:Class="FlowmeterConfigurator.Views.LoginView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:behavior="clr-namespace:CommonClassLibrary.Behaviors;assembly=CommonClassLibrary"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
prism:ViewModelLocator.AutoWireViewModel="True"
Width="330" Height="175" MaxWidth="400" MaxHeight="300" MinWidth="200" MinHeight="100">
<i:Interaction.Triggers>
<!-- OK-dialog -->
<prism:InteractionRequestTrigger SourceObject="{Binding NotificationRequest, Mode=OneWay}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True"/>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="100"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<!--UserName-->
<Label Grid.Row="0" Grid.Column="0" Content="Имя пользователя" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 30 0 0"/>
<TextBox Grid.Row="0" Grid.Column="1" Height="30" Margin="0 30 5 0" Text="{Binding Username}" AutomationProperties.AutomationId="UserNameTextBox"/>
<!--Pasword-->
<Label Grid.Row="1" Grid.Column="0" Content="Пароль" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0 10 0 0"/>
<PasswordBox Grid.Row="1" Grid.Column="1" Height="30" Margin="0 10 5 0" AutomationProperties.AutomationId="UserPasswordBox">
<i:Interaction.Behaviors>
<behavior:PasswordBoxBindingBehavior Password="{Binding Password}"/>
</i:Interaction.Behaviors>
</PasswordBox>
<!--Panel with the buttons 'Authrize' and 'Cancel'-->
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Center">
<telerik:RadButton x:Name="radButtonAuthorize" Content="Авторизоваться" Height="30" Width="Auto" Margin="5 10 20 5"
Command="{Binding PerformAuthorizationCommand}" AutomationProperties.AutomationId="AuthrizeUserButton"/>
<telerik:RadButton x:Name="radButtonCancel" Content="Отмена" Height="30" Width="Auto" Margin="0 10 5 5"
Command="{Binding СancelAuthorizationCommand}" AutomationProperties.AutomationId="CancelAuthorizationButton"/>
</StackPanel>
</Grid>
</UserControl>
The constructor of the dialog (from its ViewModel) please see below:
public LoginViewModel(IEventAggregator eventAggregator, IAuthorizationService authorizationService)
{
this._eventAggregator = eventAggregator;
this._authorizationService = authorizationService;
this.NotificationRequest = new InteractionRequest<INotification>();
this.PerformAuthorizationCommand = new DelegateCommand(this.performAuthorization, this.performAuthorizationCanExecute);
this.СancelAuthorizationCommand = new DelegateCommand(this.canсelAuthorization, this.canсelAuthorizationCanExecute);
}
As you can see from dialog's XAML above, I use the behavior (PasswordBoxBindingBehavior) to get PasswordBox' databinding possible. Below is PasswordBoxBindingBehavior class definition:
public class PasswordBoxBindingBehavior : Behavior<PasswordBox>
{
protected override void OnAttached()
{
AssociatedObject.PasswordChanged += OnPasswordBoxValueChanged;
}
public SecureString Password
{
get { return (SecureString)GetValue(PasswordProperty); }
set { SetValue(PasswordProperty, value); }
}
public static readonly DependencyProperty PasswordProperty =
DependencyProperty.Register("Password", typeof(SecureString), typeof(PasswordBoxBindingBehavior), new PropertyMetadata(null));
private void OnPasswordBoxValueChanged(object sender, RoutedEventArgs e)
{
var binding = BindingOperations.GetBindingExpression(this, PasswordProperty);
if (binding != null)
{
PropertyInfo property = binding.DataItem.GetType().GetProperty(binding.ParentBinding.Path.Path);
if (property != null)
property.SetValue(binding.DataItem, AssociatedObject.SecurePassword, null);
}
}
}
I call this dialog from Application main window (Shell). Below is XAML markup (related to the dialog). This markup is from Application main window (Shell) markup. Please see:
<prism:InteractionRequestTrigger SourceObject="{Binding LoginConfirmationRequest, Mode=OneWay}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<views:LoginView/>
</prism:PopupWindowAction.WindowContent>
<prism:PopupWindowAction.WindowStyle>
<Style TargetType="Window">
<Setter Property="ResizeMode" Value="NoResize"/>
<Setter Property="SizeToContent" Value="WidthAndHeight"/>
</Style>
</prism:PopupWindowAction.WindowStyle>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
And in this markup in the line
<views:LoginView/>
there is the following error: "No parameterless constructor defined for this object". I try to add the second constructor (that is parameterless) in ViewModel of the dialog but the error is not eliminated. Though this is only design-time error, but even so it should be eliminated. How can I correct this error?
I was having the same problem, and found that a nice solution exists if you use the recently introduced feature here.
That is, in your xaml you can do this:
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True" WindowContentType="{x:Type views:LoginView}">
<prism:PopupWindowAction.WindowStyle>
<Style TargetType="Window">
<Setter Property="ResizeMode" Value="NoResize"/>
<Setter Property="SizeToContent" Value="WidthAndHeight"/>
</Style>
</prism:PopupWindowAction.WindowStyle>
</prism:PopupWindowAction>
Instead of this:
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<views:LoginView/>
</prism:PopupWindowAction.WindowContent>
<prism:PopupWindowAction.WindowStyle>
<Style TargetType="Window">
<Setter Property="ResizeMode" Value="NoResize"/>
<Setter Property="SizeToContent" Value="WidthAndHeight"/>
</Style>
</prism:PopupWindowAction.WindowStyle>
</prism:PopupWindowAction>
This has other benefits, to do with the view being instantiated at the time of the interaction request being raised, as well.
With the following code I can create a non wrapped scrolling stackpanel of headers of a tabcontrol. It has repeat buttons that can control the scroll. I would like instead for the repeat buttons to be able to control which tab is active, and adjust the scroll so the active tab can scroll into view. Is such a thing possible?
<TabControl SelectedItem="{Binding ActiveTicketFilterTab, Mode=TwoWay}"
Margin="5"
Grid.Row="1"
BorderBrush="Gray"
BorderThickness="2"
Style="{StaticResource ResourceKey=somestyle}"
SelectionChanged="selectionchanged"
Name="somename"
Background="#252525"
ItemsSource="{Binding someobslist}"
Grid.ColumnSpan="2">
<TabControl.Resources>
<Style x:Key="TabScrollerRepeatButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Background="sc#1, 0.366693377, 0.372125238, 0.6931424">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{TemplateBinding ContentControl.Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabControl.Template>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer Panel.ZIndex="1" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<ScrollViewer.Style>
<Style TargetType="{x:Type ScrollViewer}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Margin="0,0,0,0" Grid.Row="0" Grid.Column="0" x:Name="HeaderPanel">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollContentPresenter Grid.Column="0" Content="{TemplateBinding ScrollViewer.Content}" />
<StackPanel Orientation="Horizontal" Grid.Column="1">
<RepeatButton Style="{StaticResource TabScrollerRepeatButtonStyle}" Content="<" Command="ScrollBar.LineLeftCommand"/>
<RepeatButton Style="{StaticResource TabScrollerRepeatButtonStyle}" Content=">" Command="ScrollBar.LineRightCommand"/>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ScrollViewer.Style>
<StackPanel x:Name="HeaderPanel" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1" Orientation="Horizontal"/>
</ScrollViewer>
<Border x:Name="ContentPanel"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Grid.Row="1"
Grid.Column="0"
Margin="2,-2, 0, 13"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
</ControlTemplate>
</TabControl.Template>
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem" BasedOn="{StaticResource someotherstyle}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="{x:Null}" Height="25">
<TextBlock Padding="10" Name="Title" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" HorizontalAlignment="Stretch" Text="{Binding Name}" ToolTip="{Binding Name}" />
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
<TabControl.ContentTemplate>
<DataTemplate>
<Grid Background="Transparent">
<ListBox x:Name="ticketView"
Unloaded="On_Listbox_enter"
ItemsSource="{Binding TicketsView}"
Background="#252525"
Margin="5,5,0,0"
HorizontalContentAlignment="Stretch"
BorderBrush="{x:Null}"
BorderThickness="1"
Padding="0,0,5,0"
VirtualizingPanel.IsVirtualizing="False"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.CanContentScroll="False"
ItemTemplate="{StaticResource TicketViewTemplate}">
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#252525" />
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="Transparent" />
</ListBox.Resources>
</ListBox>
<StackPanel Grid.Row="0" Margin="328,75"
Visibility="{Binding ExposeTicketSpinner, Converter={StaticResource BoolToVisConverter}}"
Width="Auto">
<Viewbox Grid.Row="0"
Height="100"
Width="100"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<WPFControls:CircularProgressBar HorizontalAlignment="Center" VerticalAlignment="Center" />
</Viewbox>
<Label Foreground="White" FontWeight="Bold" Margin="0,33" Content="Loading Tickets..." HorizontalAlignment="Center"></Label>
</StackPanel>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
To get your things to work as desired, do the following:
First
Replace your Repeatbuttons with this
<RepeatButton Style="{StaticResource TabScrollerRepeatButtonStyle}" Content="<" Command="{Binding MoveLeftCommand}"/>
<RepeatButton Style="{StaticResource TabScrollerRepeatButtonStyle}" Content=">" Command="{Binding MoveRightCommand}"/>
Second
In your ViewModel (That holds the TabControl) do it like this:
public ICommand MoveLeftCommand => new RelayCommand(x => {
var currIdx = this.someobslist.IndexOf(this.ActiveTicketFilterTab);
this.ActiveTicketFilterTab = currIdx == 0 ? this.someobslist[this.someobslist.Count - 1] : this.someobslist[currIdx - 1];
});
public ICommand MoveRightCommand => new RelayCommand(x => {
var currIdx = this.someobslist.IndexOf(this.ActiveTicketFilterTab);
this.ActiveTicketFilterTab = currIdx == this.someobslist.Count - 1 ? this.someobslist[0] : this.someobslist[currIdx + 1];
});
Thats it. In case you need a RelayCommand, i can hand you mine. Of course replace my Varaibles with yours.
This Solution circles now through the TabItems
EDIT
Here is a plain scrollcycler without navigating to the TabItem, based on this <--- IMPORTANT
Add to your ScrollViewer a Loaded-Event in XAML
Here the code:
private int _scollMoverIdx = 0;
private TabControl _container;
private void ScrollViewer_OnLoaded(object sender, RoutedEventArgs e) {
this._container = (sender as ScrollViewer).TemplatedParent as TabControl;
}
public ICommand MoveLeftCommand => new RelayCommand(x => {
if (this._scollMoverIdx == 0) {
this._scollMoverIdx = this.someobslist.Count - 1;
this._container.ScrollToCenterOfView(this.someobslist[_scollMoverIdx]);
} else {
_scollMoverIdx--;
this._container.ScrollToCenterOfView(this.someobslist[_scollMoverIdx]);
}
});
public ICommand MoveRightCommand => new RelayCommand(x => {
if (this._scollMoverIdx == this.someobslist.Count - 1) {
this._scollMoverIdx = 0;
this._container.ScrollToCenterOfView(this.someobslist[_scollMoverIdx]);
} else {
_scollMoverIdx++;
this._container.ScrollToCenterOfView(this.someobslist[_scollMoverIdx]);
}
});
Its not really pretty, but i wanted to keep it simple. The linked Extension method is greate for reusability (Credits to Ray Burns). If you want this to be MVVM-conform, i suggest making a CustomControl, since scrolling from ViewModel is breaking the pattern.
Cheers
I have a Canvas on which I have drawn a number of Rectangles which represent a number of user selected positions on the canvas.
I want to create a ToolTip for each of the rectangles that shows the x and y coords of the rectangle and also the distance to another point: the "stylus point".
The x and y coords are known when a rectangle is created of course but the distance to the stylus point is not therefore the tooltip needs to update its text each time it is shown.
I've tried using a binding as below but this just puts the text "System.Windows.Control.ToolTip" in the tool tip.
...
Rectangle rectangle = new Rectangle
{
Width = _rectWidth,
Height = _rectWidth,
Fill = new SolidColorBrush(Colors.Red)
};
rectangle.ToolTip = new ToolTip();
Binding binding = new Binding()
{
Source = this,
Path = new PropertyPath("ToolTipBinding"),
Mode = BindingMode.OneWay,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
};
BindingOperations.SetBinding( rectangle.ToolTip as ToolTip ,ToolTipService.ToolTipProperty, binding);
}
public string ToolTipBinding
{
get
{
return "How would i get the data context here (even if it bound correctly)";
}
}
Any help greatly appreciated.
This is the solution I've come up with. this will show a list of points as squares on a canvas.TargetListbelow is a list of Target objects that contain the required data for each point. Hope this helps someone.
Resource created for the ToolTips:
<UserControl.Resources>
<Style TargetType="{x:Type ToolTip}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToolTip}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Text="X:" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=PositionX}" />
<TextBox Grid.Row="1" Grid.Column="0" Text="Z:" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Path=PositionZ}" />
<TextBox Grid.Row="2" Grid.Column="0" Text="ΔX:" />
<TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Path=DeltaX}" />
<TextBox Grid.Row="3" Grid.Column="0" Text="ΔZ:" />
<TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Path=DeltaZ}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<UserControl.Resources>
Items control
<ItemsControl Grid.Row="0" Grid.Column="0" ClipToBounds="True" ItemsSource="{Binding TargetList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=PositionScreen.X}" />
<Setter Property="Canvas.Top" Value="{Binding Path=PositionScreen.Z}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding RectangleSize}"
Height="{Binding RectangleSize}"
Fill="{Binding RectangleBrush}" >
<Rectangle.ToolTip>
<ToolTip />
</Rectangle.ToolTip>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Target class, showing the property names only. These are updated as required.
public class Target : ObservableObject
{
public Point3D PositionX...
public Point3D PositionZ...
public double DeltaX...
public double DeltaZ...
public Point3D PositionScreen...
public double RectangleSize...
public Brush RectangleBrush...
}