Issue with XAML Slider control - xaml

I have the value of a slider control bound to a textblock. For some reason it works erratically. For example if I move the thumb slowly I get the right increments, but when I move it fast, It just shows some random stuff.
And here is my value changed event in C#:
private void sliderShiftStart_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
if (sliderShiftStart.Value > counter)
{
time = time.AddMinutes(30);
tbTimeDispPopup.Text = string.Format("{0:t}", time);
}
else
{
time = time.AddMinutes(-30);
tbTimeDispPopup.Text = string.Format("{0:t}", time);
}
counter = Convert.ToInt32(sliderShiftStart.Value);
}
counter is a double type variable,
time is a DateTime variable,
and tbTimeDispPopup is a textblock.
The tracking works fine when I don't do time calculations. I am trying to make time as slider values means the slider minimum is going to be 12:00 AM and the slider maximum is going to be 12:00 AM the next day.
Here is the link to my project, if somebody could have a look at it.
Project Link
Please advise.
Thanks all.

i've looked on your project, and if i correctly understand, you want to bind textbox text be bound to slider value. To do this i would suggest you the following : bind textbox text to slider value, set min max as you need, and use converter to properly display the DateTime as below.
<Grid Background="DarkSlateGray">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock x:Name="TbTimeDispPopup"
Grid.Row="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
FontSize="30"
Text="{Binding ElementName=SliderShiftStart, Path=Value, Converter={StaticResource ValueToHoursConverter}}"
TextWrapping="Wrap" />
<Slider x:Name="SliderShiftStart"
Grid.Row="1"
MinHeight="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
IsSnapToTickEnabled="True"
Maximum="48"
TickFrequency="1"/>
</Grid>
and here is the converter:
public class ValueToHoursConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
object result = DependencyProperty.UnsetValue;
if (value is double)
{
result = DateTime.Now.Date.AddHours((double)value/2d);
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Hope this helps.

Related

Use ViewLocator inside TabControl.ContentTemplate

In an AvaloniaUI window, I want to have a TabControl whose tabs are added and removed from an ObservableCollection<T>. The tab's "title" (the text appearing on the tab strip) should be set inside each item of the collection, which could belong to a different type.
For that I defined a type:
public abstract class TabViewModelBase : ViewModelBase
{
public abstract string TabHeader { get; }
}
and my collection is defined like this:
public ObservableCollection<TabViewModelBase> OpenTabs { get; } = new();
In the axaml file, this is the definition of the TabControl:
<TabControl Items="{Binding OpenTabs}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabHeader}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
So far, this works like a charm.
The problem begins when I also want to set up a container for the view inside each tab, which should not be a part of the contained view itself. I've tried by editing the xaml above and setting a ContentTemplate like this:
<TabControl Items="{Binding OpenTabs}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabHeader}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Border Child="{Binding}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
However this results in the following error:
[Binding] Error in binding to 'Avalonia.Controls.Border'.'Child': 'Could not convert 'Project.ViewModels.TestingViewModel' to 'IControl'.'
This seems to be because ViewLocator, which automatically matches a view model to a view based on its name, is not being called. I assume this is because I've defined a DataTemplate inside TabControl.ContentTemplate.
Is it possible to instruct Avalonia to use ViewLocator inside TabControl.ContentTemplate, so that a view is selected based on its name?
<Border Child="{Binding}"/>
Border expects an actual control as a child, not a view model. You need to use ContentControl instead. It can also have it's own data template or view locator.
I found a way to work around the issue, by defining an IValueConverter that uses ViewLocator internally:
public class ViewModelValueConverter : IValueConverter
{
public object? Convert(
object value, Type targetType, object parameter, CultureInfo culture
)
{
if (value == null)
return null;
if (
value is ViewModelBase viewModel
&& targetType.IsAssignableFrom(typeof(IControl))
)
{
ViewLocator viewLocator = new();
return viewLocator.Build(value);
}
throw new NotSupportedException();
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture
)
{
throw new NotSupportedException();
}
}
and using it in XAML:
<Window.Resources>
<local:ViewModelValueConverter x:Key="variableView"/>
</Window.Resources>
<TabControl Items="{Binding OpenTabs}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TabHeader}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<Border Child="{Binding, Converter={StaticResource variableView}}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
but it feels like there might be a simpler solution.

XAML converter binding for multiple properties

I want to display some text in a Xamarin forms ListView grid, based on the comparison of 2 date properties. I have a converter to compare the dates and return a string. What I am trying to do (without success) is pass the entire object to the converter.
XAML:
<ResourceDictionary>
<converters1:CancelConverter x:Key="CancelConverter" />
</ResourceDictionary>
...
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Grid.Column="0" Text="{Binding ., Converter={StaticResource CancelConverter}}" />
</Grid>
</ViewCell>
</DataTemplate>
Converter:
public class CancelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Schedule schedule = ((Schedule)value);
DateTime date1 = schedule.ProposedCollectionDate;
DateTime date2 = schedule.OrderDate;
if (date1.CompareTo(date2) < 0)
{
return "Cancel this order";
}
}
}
The problem is that in the converter, value is null. How can I pass the object to the Converter, instead of just a single property of the object?
What you have shown looks good to me, but there may be something bad in what you've hidden. I can't tell, but are you properly setting the binding context of the ListView i.e. are you setting the ItemsSource property? From the information you've given, you should be setting it to something like an ObservableCollection<Schedule> or IEnumerable<Schedule> and then each item in the ListView would have a Schedule as its binding context and it would make its way into the value converter happily.

how to change gridview row color based on condition in uwp c#?

how to change gridview row color based on condition in uwp c#?
I want to highlight the gridview row based on my conditon.
A convenient way to do this would be to put a Border around your GridViewItem and use a ValueConverter to choose the background color based on the current item.
First you define your value converter:
public class ItemToColorConverter: IValueConverter
{
//this converts the item from your data source to the color brush
//of the background of the row
public object Convert(object value, Type targetType,
object parameter, string language)
{
//cast the value parameter to the type of item in your data source
var yourValue = ( YourType )value;
if ( yourValue > 10 ) //some condition you want to use to choose the color
{
//highlight
return new SolidColorBrush( Colors.Green );
}
else
{
//leave no background
return new SolidColorBrush( Colors.Transparent );
}
}
//you don't have to implement conversion back as this is just one-way binding
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
throw new NotImplementedException();
}
}
Now you need to create a Application resource instance of the converter in App.xaml:
<Application ...>
<Application.Resources>
<converters:ItemToColorConverter x:Key="ItemToColorConverter" />
</Application.Resources>
</Application>
Now use this converter in your GridView item DataTemplate:
<GridView ItemsSource="{Binding YourDataSource"}>
<GridView.ItemTemplate>
<DataTemplate>
<Border Background="{Binding Converter={StaticResource ItemToColorConverter}">
<!-- ... your content -->
</Border>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>

UWP : Setting the Width and Height of controls as percentage values

I am trying to use a StackPanel control to display some overview options. I need to have this control using 20% of the window width when the MinWindowWidth is greater than 768px. When this is run on mobile I need the control to fill the width of the screen.
I tried as suggested in this question to try and set a value in percentage. However, I get the error - "* string cannot be converted to length".
Here is my XAML -
<StackPanel x:Name="OverviewStack">
<RelativePanel>
<StackPanel Margin="10" x:Name="User" Orientation="Horizontal">
<Ellipse Margin="5" Width="50" Height="50">
<Ellipse.Fill>
<ImageBrush>
<ImageBrush.ImageSource>
<BitmapImage UriSource="Assets/insp.jpg" />
</ImageBrush.ImageSource>
</ImageBrush>
</Ellipse.Fill>
</Ellipse>
<TextBlock Margin="5" VerticalAlignment="Center" Text="Ajay Kelkar" />
</StackPanel>
<StackPanel Margin="10" x:Name="Options" Orientation="Vertical">
<TextBlock Margin="5" Text="Sample text" />
</StackPanel>
</RelativePanel>
</StackPanel>
Is a percentage value not possible in UWP? Or am I doing something wrong?
You need to determine which platform you're currently on, and set the value accordingly.
This sounds like a converter.
Since you only have 1 project for UWP you'll need to check in the converter which platform you're on. such info can be acquired as such:
if (AnalyticsInfo.VersionInfo.DeviceFamily == "Windows.Mobile")
{
//Act accordingly
}
So I think you'd like to do something like this:
public object Convert(object value, Type targetType, object parameter, string language)
{
// bind the width as the value the converter is set upon
var width = double.Parse(value.ToString());
if (AnalyticsInfo.VersionInfo.DeviceFamily == "Windows.Mobile")
{
return width;
}
// You weren't clear exactly regarding the 768px thing so act accordingly here
return width * 0.2d;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}

Binding ListPicker.SelectedIndex problem

I'm trying to do a two way binding of the SelectedIndex attribute of a ListPicker in a Windows Phone 7 UserControl.
It raises the following exception when I set the DataContext:
SelectedIndex must always be set to a valid value.
This is the XAML code
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<toolkit:ListPicker
Grid.Row="0"
x:Name="List1"
SelectionChanged="Picker_SelectionChanged"
SelectedIndex="{Binding PickerSelectedIndex, Mode=TwoWay}"
ItemTemplate="{StaticResource PickerTemplate}"
ItemsSource="{Binding MyList}"/>
</Grid>
And the code behind in DataContext
private ObservableCollection<MyClass> myList = null;
public ObservableCollection<MyClass> MyList
{
get { return this.myList; }
set
{
if (value != this.myList)
{
this.myList= value;
NotifyPropertyChanged("MyList");
this.PickerSelectedIndex = 0;
}
}
}
private int pickerSelectedIndex = 0;
public int PickerSelectedIndex
{
get
{
return this.pickerSelectedIndex;
}
set
{
this.pickerSelectedIndex= value;
}
}
Putting a breakpoint in PickerSelectedIndex.get I can see that it is returned correctly (0).
I am sure that the problem is SelectedIndex="{Binding PickerSelectedIndex, Mode=TwoWay}" because deleting this line solves the problem, and I can see the ListPicker correctly loaded with the data from MyList.
I can't see where is the problem...
Moving SelectedIndex after ItemsSource solved the problem.
This is the working snippet
<toolkit:ListPicker
Grid.Row="0"
x:Name="List1"
SelectionChanged="Picker_SelectionChanged"
ItemTemplate="{StaticResource PickerTemplate}"
ItemsSource="{Binding MyList}"
SelectedIndex="{Binding PickerSelectedIndex, Mode=TwoWay}"/>
Anyone have an explanation for this?
My guess would be that the binding is being applied with a default value of zero when created and before the items exist. It's therefore trying to select the first item (with a zero index) before that item is created.
Try making sure that the ViewModel property of PickerSelectedIndex defaults to -1.
You may also want to delay setting the binding until the items are created.
Matt Lacey is right; binding happens before the data items get populated & hence the error. If you have an event handler for SelectionChanged, you will notice that a breakpoint in it will be hit as the page/listpicker loads. Here is one way to get around this initialization issue:
private void SomeListPicker_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Make sure we don't handle the event during initiation.
if (e.RemovedItems != null && e.RemovedItems.Count > 0)
{
if (this.SomeListPicker.SelectedItem != null)
{
// Do actual stuff.
}
}
}
I had the same issue and the ordering of the properties in XAML didn't help. In my case, I was binding ItemsSource to a property on a StaticResource, but I was binding SelectedIndex with a property of my page's ViewModel. The moment I changed the binding of ItemsSource to bind to a property on the ViewModel itself (i.e. duplicated a property of the StaticResource), my issue went away and I was able to perform 2-way binding on SelectedIndex as well.
I've found the same problem with my app.
But I noticed that it happens when I delete all the elements of the list bounded to the ListPicker in the ViewModel.
So it isn't necessary to Bind SelectedIndex with another property since the problem depends only on the list bounded.
Here is my code which work fine for me:
<toolkit:ListPicker x:Name="ListaCorsi"
SelectionChanged="ListaCorsi_SelectionChanged"
ItemsSource="{Binding ListaCorsiUser}"
SelectionMode="Single"
ItemTemplate="{StaticResource CorsiDataTemplate}"
ItemsPanel="{StaticResource ItemsPanelTemplateListaCorsi}"/>
The list in view model:
private ObservableCollection<Corsi> _listaCorsiUser;
public ObservableCollection<Corsi> ListaCorsiUser
{
get { return _listaCorsiUser; }
set
{
_listaCorsiUser = value;
OnPropertyChanged("ListaCorsiUser");
}
}
The handler for SelectionChanged:
void ListaCorsi_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ListaCorsi.SelectedItem != null)
{
---
this.CorsoSelected = ListaCorsi.SelectedItem as Corsi;
}
}
Where Corsi is the class type of the list.
Here ListPicker template:
<DataTemplate x:Key="CorsiDataTemplate">
<Grid>
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#FF3523FF" Offset="0.25"/>
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="#FF3523FF" Offset="0.75"/>
</LinearGradientBrush>
</Grid.Background>
<TextBlock TextWrapping="Wrap" Text="{Binding NomeCorso}" FontSize="24" FontFamily="Freestyle Script" TextAlignment="Center"/>
</Grid>
</DataTemplate>
And, in the end, the method delete that checks if the list returned by IsolatedStorage is empty, if so, I put a fake empty element in the list bounded to the ListPicker, in order to not receive the error mentioned in this post.
if (this.CorsoSelected != null)
{
---
List<Corsi> corsi = new List<Corsi>(DBHelper.GetCorsiByUserId(PassaggioValori.UserId));
if (corsi.Count > 0)
{
this.ListaCorsiUser = new ObservableCollection<Corsi>(corsi);
}
else
{
this.ListaCorsiUser = new ObservableCollection<Corsi>(new List<Corsi>() { new Corsi()});
}
----
}
The strange thing was that, if the list was empty when the page has been loaded, nothing happens, instead, when I removed the last element from the list, the application raised the exception "SelectedItem must always be set to a valid value".
Now the problem is solved.