I'm a Flutter developer and my company is considering moving over to .NET MAUI, so I spent the weekend trying it out but came across a flaw that I cant seem to get around. Using Xamarin.Essentials to get the screen size with
Global.height = Microsoft.Maui.Devices.DeviceDisplay.MainDisplayInfo.Height and Width
I get a return of the actual screen size in pixels be it from my attached S10 Ultra or the Pixel 5 Emulator. However this is not the actual size the debugger is using to draw on my screen so if I describe a View (Widget) as being Global.height *.5 I get a drawn height on my screen of about 70 - 75% of the actual screen height.
Is there a way around this? or a setting I am missing? It's quite important that this works correctly in order to make a truly responsive application.
MainPage.xmal
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:mct="clr-namespace:CommunityToolkit.Maui.Behaviors;assembly=CommunityToolkit.Maui"
x:Class="Bears_Portal.MainPage">
<ContentPage.Behaviors>
<mct:StatusBarBehavior x:Name="statusBar"/>
</ContentPage.Behaviors>
<AbsoluteLayout>
<Image
Source="ambulance.jpeg"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
Aspect="AspectFill">
</Image>
<Rectangle
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All"
Opacity=".85">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0,1">
<GradientStop Color="#1A2373"
Offset="0.01" />
<GradientStop Color="#3949AB"
Offset="1.0" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<BoxView
AbsoluteLayout.LayoutBounds="0,0,1,.045"
AbsoluteLayout.LayoutFlags="All"
Color="White"
CornerRadius="0,0,10,10">
<BoxView.Shadow>
<Shadow
Brush="Black"
Offset="0,5"
Radius="40"
Opacity="1">
</Shadow>
</BoxView.Shadow>
</BoxView>
<BoxView
x:Name="BottomSheet"
AbsoluteLayout.LayoutBounds=".04,1,1,0"
AbsoluteLayout.LayoutFlags="All"
Color="White"
CornerRadius="10,10,0,0">
<BoxView.Shadow>
<Shadow
Brush="Black"
Offset="0,-5"
Radius="40"
Opacity="1">
</Shadow>
</BoxView.Shadow>
</BoxView>
<Border
AbsoluteLayout.LayoutBounds=".99,0,.15,.04"
AbsoluteLayout.LayoutFlags="All"
Stroke="#1A2373"
StrokeThickness="1"
StrokeShape="RoundRectangle 5">
<Border.Background>
<LinearGradientBrush EndPoint="0,1">
<GradientStop Color="#7986CB"
Offset="0" />
<GradientStop Color="#3949AB"
Offset="1.0" />
</LinearGradientBrush>
</Border.Background>
<Border.Shadow>
<Shadow
Brush="Black"
Offset="2,5"
Radius="5"
Opacity="0.6"
/>
</Border.Shadow>
<Label
Text="REFRESH"
FontSize="Micro"
TextColor="White"
HorizontalOptions="Center"
VerticalOptions="Center">
<Label.Shadow>
<Shadow
Brush="Black"
Offset="0,5"
Radius="2"
Opacity="0.8"
/>
</Label.Shadow>
</Label>
<Border.GestureRecognizers>
<TapGestureRecognizer Tapped="Refresh"/>
</Border.GestureRecognizers>
</Border>
<Label
AbsoluteLayout.LayoutBounds=".5,0,.6,.04"
AbsoluteLayout.LayoutFlags="All"
Text="BEARS EMPLOYEE PORTAL"
HorizontalOptions="Center"
VerticalOptions="Center">
<Label.Shadow>
<Shadow
Brush="Black"
Offset="2,2"
Radius="5"
Opacity="0.6"
/>
</Label.Shadow>
</Label>
<Border
AbsoluteLayout.LayoutBounds=".01,0,.15,.04"
AbsoluteLayout.LayoutFlags="All"
Stroke="#1A2373"
StrokeThickness="1"
StrokeShape="RoundRectangle 5">
<Border.Background>
<LinearGradientBrush EndPoint="0,1">
<GradientStop Color="#7986CB"
Offset="0" />
<GradientStop Color="#3949AB"
Offset="1.0" />
</LinearGradientBrush>
</Border.Background>
<Border.Shadow>
<Shadow
Brush="Black"
Offset="2,5"
Radius="5"
Opacity="0.6"
/>
</Border.Shadow>
<Label
Text="MENU"
FontSize="Micro"
TextColor="White"
HorizontalOptions="Center"
VerticalOptions="Center">
<Label.Shadow>
<Shadow
Brush="Black"
Offset="0,5"
Radius="2"
Opacity="0.8"
/>
</Label.Shadow>
</Label>
<Border.GestureRecognizers>
<TapGestureRecognizer Tapped="Menu"/>
</Border.GestureRecognizers>
</Border>
</AbsoluteLayout>
</ContentPage>
MainPage.xmal.cs
public partial class MainPage : ContentPage
{
bool bottomSheet = false;
public MainPage()
{
InitializeComponent();
statusBar.StatusBarColor = Colors.White;
statusBar.StatusBarStyle = CommunityToolkit.Maui.Core.StatusBarStyle.DarkContent;
}
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
Global.height = height;
Global.width = width;
}
private void Refresh(object sender, EventArgs e)
{
bottomSheet = !bottomSheet;
}
private void Menu(object sender, EventArgs e)
{
bottomSheet = !bottomSheet;
if (bottomSheet) { BottomSheet.HeightRequest = Global.height*.5; } else { BottomSheet.HeightRequest = Global.height * .04; }
Console.WriteLine($"HEIGHT {Global.height}");
Console.WriteLine($"Width {Global.width}");
}
}
PIXEL 5 Android 12.1 API 32
Related
I'm using a WebView control on a page in my MAUI app and I would like the content of the page (a video) to scale to fit the container size. Currently it spills over and it's not obvious (or useful really) that a user would have to scroll the video up and down and side to see it. Can the content be scaled to fit the container dimensions?
Here's a short example based on standard new .MAUI project. This is the page markup:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp2.MainPage">
<Grid
x:Name="MainGrid"
RowDefinitions="50,*"
ColumnDefinitions="*">
<Button
Grid.Row="0"
Grid.Column="0"
x:Name="CounterBtn"
HeightRequest="40"
Text="Click me"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
<VerticalStackLayout
Grid.Row="1"
Grid.Column="0"
Spacing="25"
Padding="30,0">
<Border
Grid.Row="1"
Grid.Column="0"
Grid.RowSpan="3"
Stroke="Black"
StrokeThickness="3"
x:Name="WebBorder">
<Border.StrokeShape>
<RoundRectangle CornerRadius="5" />
</Border.StrokeShape>
<VerticalStackLayout
BackgroundColor="White">
<Image
x:Name="CloseBtn"
Margin="5,5,0,0"
HorizontalOptions="Start"
Source="close_button.png">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="CloseButton_Clicked"/>
</Image.GestureRecognizers>
</Image>
<WebView
x:Name="WebView"/>
</VerticalStackLayout>
</Border>
</VerticalStackLayout>
</Grid>
</ContentPage>
The only thing I don't have for you (since I can't add an attachment) is the close button png file. This is stuff added to the code behind:
private async void OnCounterClicked(object sender, EventArgs
e)
{
//display the container
WebView.IsVisible= true;
WebView.HeightRequest =
DeviceDisplay.Current.MainDisplayInfo.Height - 60;
WebView.WidthRequest =
DeviceDisplay.Current.MainDisplayInfo.Width - 20;
WebView.Margin = new Thickness(5, 0, 5, 0);
//show the video
WebView.Source =
"https://www.youtube.com/shorts/JSIqFdb4KQ8";
WebView.HeightRequest = WebView.HeightRequest -
CloseBtn.Height - 10;
for (int i = 0; i < 2; i++)
{
await CloseBtn.RotateTo(180, 200);
await CloseBtn.RotateTo(0, 200);
}
}
private void CloseButton_Clicked(object sender, EventArgs e)
{
//hide it a
WebView.IsVisible= false;
}
When the video plays it is much larger than the viewport of the device. I'm looking for a method or way to scale the content contained in the WebView to be 100% of the WebView's size.
Do you mean the space of the VerticalStackLayout of your code?
I tried to remove the Padding and Spacing of the VerticalStackLayout in your code,then there is no such problem.
Spacing="25"
Padding="30,0"
You can refer to the following code I tested on my side:
<Grid
x:Name="MainGrid"
BackgroundColor="Red"
RowDefinitions="50,*"
ColumnDefinitions="*"
>
<Button
Grid.Row="0"
Grid.Column="0"
x:Name="CounterBtn"
HeightRequest="40"
Text="Click me"
HorizontalOptions="Center" />
<VerticalStackLayout
Grid.Row="1"
Grid.Column="0"
BackgroundColor="Blue">
<Border
Grid.Row="1"
Grid.Column="0"
Grid.RowSpan="3"
Stroke="Black"
StrokeThickness="3"
x:Name="WebBorder">
<Border.StrokeShape>
<RoundRectangle CornerRadius="5" />
</Border.StrokeShape>
<VerticalStackLayout
BackgroundColor="White">
<Image
BackgroundColor="Yellow"
x:Name="CloseBtn"
Margin="5,5,0,0"
HorizontalOptions="Start"
Source="dotnet_bot.svg">
</Image>
<!--<WebView
x:Name="WebView"/>-->
<WebView HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand" Source="https://dotnet.microsoft.com/apps/xamarin" />
</VerticalStackLayout>
</Border>
</VerticalStackLayout>
</Grid>
I need to use a TemplateSelector for the ControlTemplate in TemplatedView. Is there any way to handle ControlTemplateSelector, similar to how DataTemplateSelector is done?
<ContentPage.Resources>
<ResourceDictionary>
<data:MediaTemplateSelector x:Key="headerTemplateSelector"
VideoTemplate="{StaticResource VideoHeaderTemplate}"
ImageTemplate="{StaticResource ImageHeaderTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<TemplatedView ControlTemplate="{StaticResource headerTemplateSelector}"/>
</ContentPage.Content>
ControlTemplate and DataTemplate works in a different manner.
I started with ControlTemplates trying to do something similar to DataTemplateSelector but defining an item appearance at page level instead at item in a collection like a ListView or CollectionView. So confusing at the beginning because it works totally different... first: forget the concept of DataTempateSelector, has nothing to do with ControlTemplates.
The code i will present has some properties/class names in spanish, hope you understand the idea anyway. First, suppose you have a property on your ViewModel that needs to be presented in a different manner depending on the same or other ViewModel property.
First, define a ContentView class that later you will put as a part of your page (could be a TemplatedView, but, i'm getting an exception when i try to put the templatedView directly on a page, so, i use a ContentView):
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local1="clr-namespace:MrWaiter.Resources;assembly=MrWaiter"
x:Class="MrWaiter.Views.ComandaControlTemplate"
ControlTemplate="{StaticResource ComandaPropia}">
<ContentView.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="ComandaPropia">
<Frame BackgroundColor="{x:StaticResource Key=pLight}" CornerRadius="10" HasShadow="True" HeightRequest="32"
Padding="0" Margin="0">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label FontSize="Large"
HorizontalOptions="StartAndExpand"
TextColor="White"
Margin="5,0,0,0"
BindingContext="{TemplateBinding BindingContext}"
Text="{TemplateBinding Parent.BindingContext.Comanda.Numero, StringFormat='Comanda {0}'}">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="2"
BindingContext="{TemplateBinding BindingContext}"
Command="{TemplateBinding Parent.BindingContext.MostrarComandasCommand}" >
</TapGestureRecognizer>
</Label.GestureRecognizers>
</Label>
<Button FontFamily="{StaticResource MaterialFontFamily}"
HorizontalOptions="End" x:Name="botonExpandir"
Text="{x:Static local1:IconFont.Play}" BackgroundColor="Transparent"
Rotation="-90" FontSize="32" TextColor="White"
WidthRequest="55"
Margin="0,0,0,0"
Padding="0,0,0,0"
Command="{TemplateBinding MaximizeCommand}"
VerticalOptions="CenterAndExpand"
>
</Button>
</StackLayout>
</Frame>
</ControlTemplate>
<ControlTemplate x:Key="ComandaInvitaA" >
<Frame BackgroundColor="DarkGray" CornerRadius="10" HasShadow="True" HeightRequest="32"
Padding="0" Margin="0">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label FontSize="Large"
HorizontalOptions="StartAndExpand"
TextColor="White"
Margin="5,0,0,0"
BindingContext="{TemplateBinding BindingContext}"
Text="{TemplateBinding Parent.BindingContext.Comanda.Numero, StringFormat='Comanda {0}'}">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="2"
BindingContext="{TemplateBinding BindingContext}"
Command="{TemplateBinding Parent.BindingContext.MostrarComandasCommand}" >
</TapGestureRecognizer>
</Label.GestureRecognizers>
</Label>
<Button FontFamily="{StaticResource MaterialFontFamily}"
HorizontalOptions="End" x:Name="botonExpandir" x:FieldModifier="Public"
Text="{x:Static local1:IconFont.Play}" BackgroundColor="Transparent"
Rotation="-90" FontSize="32" TextColor="White"
WidthRequest="55"
Margin="0,0,0,0"
Padding="0,0,0,0"
BindingContext="{TemplateBinding BindingContext}"
Command="{TemplateBinding Parent.BindingContext.MaximizeCommand}"
VerticalOptions="CenterAndExpand"
>
</Button>
</StackLayout>
</Frame>
</ControlTemplate>
<ControlTemplate x:Key="ComandaInvitadaPor">
<Frame BackgroundColor="{x:StaticResource Key=sColor}" CornerRadius="10" HasShadow="True" HeightRequest="32"
Padding="0" Margin="0">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label FontSize="Large"
HorizontalOptions="StartAndExpand"
TextColor="White"
Margin="5,0,0,0"
BindingContext="{TemplateBinding BindingContext}"
Text="{TemplateBinding Parent.BindingContext.Comanda.Numero, StringFormat='Comanda {0}'}">
<Label.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="2"
BindingContext="{TemplateBinding BindingContext}"
Command="{TemplateBinding Parent.BindingContext.MostrarComandasCommand}" >
</TapGestureRecognizer>
</Label.GestureRecognizers>
</Label>
<Switch IsToggled="{Binding MostrarDetallado}"
IsVisible="False"
OnColor="{StaticResource Key=sLight}"
ThumbColor="{StaticResource Key=sDark}"
></Switch>
<Button FontFamily="{StaticResource MaterialFontFamily}"
HorizontalOptions="End" x:Name="botonExpandir"
Text="{x:Static local1:IconFont.Play}" BackgroundColor="Transparent"
Rotation="-90" FontSize="32" TextColor="White"
WidthRequest="55"
Margin="0,0,0,0"
Padding="0,0,0,0"
Command="{TemplateBinding MaximizeCommand}"
VerticalOptions="CenterAndExpand"
>
</Button>
</StackLayout>
</Frame>
</ControlTemplate>
</ResourceDictionary>
</ContentView.Resources>
</ContentView>
The important stuff is this property of contentView, that defines a kind of ControlTemplate default value:
ControlTemplate="{StaticResource ComandaPropia}"
And pay attention on how the Data Binding is defined inside the ControlTemplates, using TemplatedBindings instead Bindings:
<Label FontSize="Large"
HorizontalOptions="StartAndExpand"
TextColor="White"
Margin="5,0,0,0"
BindingContext="{TemplateBinding BindingContext}"
Text="{TemplateBinding Parent.BindingContext.Comanda.Numero, StringFormat='Comanda {0}'}">
On the ContentView Code Behind you must create a BindableProperty to allow DataBinding between a property on View and a property on ViewModel (maybe it could be done with DataTriggers, but im's learning Xamaring and i want to practice with BindableProperties). That Property, will be responsible to select the ControlTemplate, pay attention to Method HandleTipoComandaPropertyChanged, its where we select the ControlTemplate on propertyChanged:
public partial class ComandaControlTemplate : ContentView
{
public static BindableProperty TipoComandaProperty = BindableProperty.Create(
propertyName: "TipoComanda",
returnType: typeof(Controls.TipoComandaEnum),
declaringType: typeof(ComandaControlTemplate),
defaultValue: Controls.TipoComandaEnum.Propia,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: HandleTipoComandaPropertyChanged);
private static void HandleTipoComandaPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var targetView = (ComandaControlTemplate)bindable;
if (targetView != null)
{
if ((Controls.TipoComandaEnum)newValue != (Controls.TipoComandaEnum)oldValue)
{
if ((Controls.TipoComandaEnum)newValue== Controls.TipoComandaEnum.Propia)
targetView.ControlTemplate =(ControlTemplate)targetView.Resources["ComandaPropia"];
else if ((Controls.TipoComandaEnum)newValue == Controls.TipoComandaEnum.Invitada)
targetView.ControlTemplate = (ControlTemplate)targetView.Resources["ComandaInvitadaPor"];
else if ((Controls.TipoComandaEnum)newValue == Controls.TipoComandaEnum.Invitacion)
}
}
}
public Controls.TipoComandaEnum TipoComanda
{
get
{
return Controls.TipoComandaEnum)base.GetValue(TipoComandaProperty);
}
set
{
base.SetValue(TipoComandaProperty, value);
}
}
public ComandaControlTemplate()
{
InitializeComponent();
}
}
Finally, in your page, place your templated ContentView whenever you need and bind the BindableProperty we created in View to the property on ViewModel that will determine the ControlTemplate presented:
<views:ComandaControlTemplate TipoComanda="{Binding BindingContext.TipoComanda}"></views:ComandaControlTemplate>
Note that TipoComanda is the BindableProperty we created on templatedView, and it as an equivalent property on ViewModel.
Note: that's a general idea on how can we use ControlTemplates, it works, but maybe there are better ways to acomplish.
I'm relatively new at WPF and C#. Following this example, I have created a form with a datagrid and two comboboxes (FileName and PartID) that I'd like to use to filter the datagrid (surveyresponsesDataGrid).
Here is my XAML for the WPF Form:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dat="clr-namespace:System.Windows.Data;assembly=PresentationFramework"
Title="Review Data" Height="446" Width="987" mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:my="clr-namespace:SWA_IFR" Loaded="Window_Loaded">
<Window.Resources>
<CollectionViewSource
Source="{Binding ElementName=surveyresponses, Path=FileName.SelectedItem}"
x:Key="cvs" Filter="fn_Filter"
x:Name="srView"
CollectionViewType="{x:Type dat:ListCollectionView}" />
</Window.Resources>
<Grid>
<DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True" Height="247" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource cvs}}" Margin="12,93,0,0" Name="surveyresponsesDataGrid"
RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top" Width="941">
<DataGrid.Columns>
<DataGridTextColumn x:Name="fileUploadNameColumn" Binding="{Binding Path=FileUploadName}" Header="File Upload Name" Width="*" />
<DataGridTextColumn x:Name="participantIDColumn" Binding="{Binding Path=ParticipantID}" Header="Participant ID" Width="*" />
<DataGridTextColumn x:Name="quesIDColumn" Binding="{Binding Path=QuesID}" Header="Ques ID" Width="*" />
<DataGridTextColumn x:Name="quesTypeColumn" Binding="{Binding Path=QuesType}" Header="Ques Type" Width="*" />
<DataGridTextColumn x:Name="scoreIntColumn" Binding="{Binding Path=ScoreInt}" Header="Score Int" Width="*" />
<DataGridTextColumn x:Name="scoreTextColumn" Binding="{Binding Path=ScoreText}" Header="Score Text" Width="*" />
<DataGridTextColumn x:Name="scoreDescrColumn" Binding="{Binding Path=ScoreDescr}" Header="Score Descr" Width="*" />
</DataGrid.Columns>
</DataGrid>
<ComboBox Height="29" HorizontalAlignment="Left" Margin="184,17,0,0" Name="FileName" VerticalAlignment="Top" Width="211" SelectionChanged="FileName_SelectionChanged" />
<Label Content="Filter by File Upload Name" Height="29" HorizontalAlignment="Left" Margin="14,17,0,0" Name="label2" VerticalAlignment="Top" Width="159" />
<ComboBox Height="29" HorizontalAlignment="Left" Margin="184,52,0,0" Name="PartID" VerticalAlignment="Top" Width="211" />
<Label Content="Filter by Participant ID" Height="29" HorizontalAlignment="Left" Margin="14,52,0,0" Name="label1" VerticalAlignment="Top" Width="159" />
</Grid>
Here is my code:
public partial class ReviewData : Window
{
SMEntities context = new SMEntities();
public ReviewData()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var filename = context.surveyresponses.Select(f => f.FileUploadName).Distinct();
this.FileName.ItemsSource = filename;
var partid = context.surveyresponses.Select(p => p.ParticipantID).Distinct();
this.PartID.ItemsSource = partid;
}
void fn_Filter(object sender, FilterEventArgs e)
{
if (e.Item is surveyresponse)
e.Accepted = (e.Item as surveyresponse).FileUploadName.ToUpper().Contains(FileName.Text.ToUpper());
else
e.Accepted = true;
}
private void RefreshList()
{
if (surveyresponsesDataGrid.Items is CollectionView)
{
CollectionViewSource csv = (CollectionViewSource)FindResource("cvs");
if (csv != null)
csv.View.Refresh();
}
}
private void srFilter_TextChanged(object sender, TextChangedEventArgs e)
{
RefreshList();
}
} }
First problem is that I don't think the CollectionViewSource is correctly setup in the Window.Resource section, but I can't find any clear guidance about what's off. The EF class is surveyresponses but not sure that the I've correctly set the Path attribute -- currently it is set to the SelectedItem of the first combobox FileName. My goal is to filter the datagrid for only the entries that match the selection in the FileName combobox.
Second problem is that I'm uncertain how I would go about having a secondary filter, such that once the first filter is set, that selecting the PartId combobox will further filter the datagrid based on the second field (Participant ID).
I'm missing something fundamental and don't see what it is.
Thanks for the guidance.
I have a problem with my MVVM Light implementation where I have my WPF Toolkit DataGrid and Data context bound to the correct object but no data is showing up. Can someone tell me what I'm doing wrong?
Here is my code:
MainViewModel.cs
public class MainViewModel : ViewModelBase
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private C.Wsi.ClientSession privateSession;
private ObservableCollection<CashAccount> _cashaccounts;
public ObservableCollection<CashAccount> CashAccounts
{
get { return _cashaccounts; }
set
{
if (_cashaccounts.Equals(value))
{
return;
}
_cashaccounts = value;
RaisePropertyChanged("CashAccounts");
}
}
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
if (IsInDesignMode)
{
// Code runs in Blend --> create design time data.
}
else
{
_cashaccounts = new ObservableCollection<CashAccount>();
// Subscribe to CollectionChanged event
_cashaccounts.CollectionChanged += OnCashAccountListChanged;
logger.Info("----- Start -----");
// Code runs "for real"
Cs.Helper.Session session = new Session();
privateSession = session.getSession();
logger.Info("Private Session: " + privateSession.GetHashCode());
logger.Info("...Connected.....");
Cs.Helper.ResultSet results = new ResultSet();
PositionBean[] pos = results.getPositions(privateSession);
logger.Info("Positions returned: " + pos.Length);
SecurityBean[] secs = results.getSecurities(privateSession);
logger.Info("Securities returned: " + secs.Length);
ArrayBuilder ab = new ArrayBuilder(pos, secs);
CashAccount c = new CashAccount();
c.qtySod = 100.00;
c.name = "Hi";
c.account = "Acct1";
c.cashAmount = 67.00;
_cashaccounts.Add(c);
RaisePropertyChanged("CashAccounts");
//this._cashaccounts=ab.joinPositionSecurities();
}
}
void OnCashAccountListChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
}
////public override void Cleanup()
////{
//// // Clean up if needed
//// base.Cleanup();
////}
}
MainWindow.xaml
<Window x:Class="CreditSuisse.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:CreditSuisse.Helper"
xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
mc:Ignorable="d"
Height="301"
Width="520"
Title="Credit Suisse - Custodial Cash Application"
DataContext="{Binding Path=Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
<local:VisibilityConverter x:Key="VisibilityConverter" />
<!-- DataGrid Background -->
<LinearGradientBrush x:Key="BlueLightGradientBrush" StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#FFEAF3FF"/>
<GradientStop Offset="0.654" Color="#FFC0DEFF"/>
<GradientStop Offset="1" Color="#FFC0D9FB"/>
</LinearGradientBrush>
<!-- DatGrid style -->
<Style TargetType="{x:Type dg:DataGrid}">
<Setter Property="Margin" Value="5" />
<Setter Property="Background" Value="{StaticResource BlueLightGradientBrush}" />
<Setter Property="BorderBrush" Value="#FFA6CCF2" />
<Setter Property="RowBackground" Value="White" />
<Setter Property="AlternatingRowBackground" Value="#FDFFD0" />
<Setter Property="HorizontalGridLinesBrush" Value="Transparent" />
<Setter Property="VerticalGridLinesBrush" Value="#FFD3D0" />
<Setter Property="RowHeaderWidth" Value="0" />
</Style>
<!-- Enable rows as drop targets -->
<Style TargetType="{x:Type dg:DataGridRow}">
<Setter Property="AllowDrop" Value="True" />
</Style>
</ResourceDictionary>
</Window.Resources>
<dg:DataGrid DataContext="{Binding MainViewModel}" ItemsSource="{Binding CashAccounts}">
Margin="5" AutoGenerateColumns="True" HorizontalScrollBarVisibility="False">
<dg:DataGrid.Columns>
<dg:DataGridTextColumn Binding="{Binding account}" Header="Account Code" />
<dg:DataGridTextColumn Binding="{Binding name}" Header="Security Name" />
<dg:DataGridTextColumn Binding="{Binding cashAmount}" Header="Quantity Start of Day" />
<dg:DataGridTextColumn Binding="{Binding price}" Header="Cash Delta (Price Delta)" />
<dg:DataGridTemplateColumn Header="Action">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Get" Visibility="{Binding cashChanged, Converter={StaticResource VisibilityConverter}}" Background="Red" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
</dg:DataGrid.Columns>
</dg:DataGrid>
When you do DataContext="{Binding Path=Main, Source={StaticResource Locator}}" in your Window and after DataContext="{Binding MainViewModel}" in the Datagrid, WPF tries to bind to a property named MainViewModel on your MainViewModel class which will fail. You can confirm this by looking for the binding error in the Output window in VS.
Just remove DataContext="{Binding MainViewModel}" from the DataGrid and it should work.
EDIT:
There is an extra closing > at the end of the first line in your XAML:
<dg:DataGrid DataContext="{Binding MainViewModel}" ItemsSource="{Binding CashAccounts}">
Margin="5" AutoGenerateColumns="True" HorizontalScrollBarVisibility="False">
It should be:
<dg:DataGrid DataContext="{Binding MainViewModel}" ItemsSource="{Binding CashAccounts}"
Margin="5" AutoGenerateColumns="True" HorizontalScrollBarVisibility="Disabled">
Note: False is not a valid value for HorizontalScrollBarVisibility it supports Auto, Disabled, Hidden, Visible
But I don't know why WPF throws Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead for your original XAML instead of some more meaningful error.
I just want to ask for your opinion on how to achieve the 'Slide to Unlock' feature from iPhone using Windows Presentation Foundation.
I already came across to this article: iPhone slide to unlock progress bar (part 1), and wondering if you can give me some other resources for a good head start. Thank you.
I would retemplate a Slider, as this is the closest control, functionality-wise.
You should catch the event of Value_Changed, and if Value == Maximum then the slider is "opened".
Retemplating the control would make it look like your "unlock control" with ease. I'll paste later an example.
-- EDIT --
Have free time at work, so I started it for you.
The usage is as follows:
<Grid x:Name="LayoutRoot">
<Slider Margin="185,193,145,199" Style="{DynamicResource SliderStyle1}"/>
</Grid>
and the ResourceDictionary:
<ResourceDictionary
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" mc:Ignorable="d">
<LinearGradientBrush x:Key="MouseOverBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#AAA" Offset="1.0"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="LightBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF" Offset="0.0"/>
<GradientStop Color="#EEE" Offset="1.0"/>
</LinearGradientBrush>
<LinearGradientBrush x:Key="NormalBorderBrush" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#CCC" Offset="0.0"/>
<GradientStop Color="#444" Offset="1.0"/>
</LinearGradientBrush>
<Style x:Key="SimpleScrollRepeatButtonStyle" d:IsControlPart="True" TargetType="{x:Type RepeatButton}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Grid>
<Rectangle Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="{TemplateBinding BorderThickness}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ThumbStyle1" d:IsControlPart="True" TargetType="{x:Type Thumb}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Grid Width="54">
<Ellipse x:Name="Ellipse" />
<Border CornerRadius="10" >
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFFBFBFB" Offset="0.075"/>
<GradientStop Color="Gainsboro" Offset="0.491"/>
<GradientStop Color="#FFCECECE" Offset="0.509"/>
<GradientStop Color="#FFA6A6A6" Offset="0.943"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="{StaticResource MouseOverBrush}" TargetName="Ellipse"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" Value="{StaticResource DisabledBackgroundBrush}" TargetName="Ellipse"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="SliderStyle1" TargetType="{x:Type Slider}">
<Setter Property="Background" Value="{StaticResource LightBrush}"/>
<Setter Property="BorderBrush" Value="{StaticResource NormalBorderBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Border CornerRadius="14" Padding="4">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF252525" Offset="0"/>
<GradientStop Color="#FF5C5C5C" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<Grid x:Name="GridRoot">
<TextBlock Text="Slide to unlock" HorizontalAlignment="Center" VerticalAlignment="Center" />
<!-- TickBar shows the ticks for Slider -->
<!-- The Track lays out the repeat buttons and thumb -->
<Track x:Name="PART_Track" Height="Auto">
<Track.Thumb>
<Thumb Style="{StaticResource ThumbStyle1}"/>
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Style="{StaticResource SimpleScrollRepeatButtonStyle}" Command="Slider.IncreaseLarge" Background="Transparent"/>
</Track.IncreaseRepeatButton>
<Track.DecreaseRepeatButton>
<RepeatButton Style="{StaticResource SimpleScrollRepeatButtonStyle}" Command="Slider.DecreaseLarge" d:IsHidden="True"/>
</Track.DecreaseRepeatButton>
</Track>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="TickPlacement" Value="TopLeft"/>
<Trigger Property="TickPlacement" Value="BottomRight"/>
<Trigger Property="TickPlacement" Value="Both"/>
<Trigger Property="IsEnabled" Value="false"/>
<!-- Use a rotation to create a Vertical Slider form the default Horizontal -->
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="LayoutTransform" TargetName="GridRoot">
<Setter.Value>
<RotateTransform Angle="-90"/>
</Setter.Value>
</Setter>
<!-- Track rotates itself based on orientation so need to force it back -->
<Setter TargetName="PART_Track" Property="Orientation" Value="Horizontal"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Note that this is a very good start, but it's not everything.
I would also define a custom control that derives from slider and that uses this style automatically. Also I would expose a SlideUnlocked event when the user slides all the way right.
To finish it all i would also add an animation that moves the Thumb back left in case the user has dragged it right, but not all the way (to imitate iPhone's UX exactly.)
Good luck, and ask away if you don't know how to implement any of the stages i suggested.
WPF slider has got one "-" and it is value ,
always when you move it, value is for example in decimal 1,122213174 so one "-".
But another way to create slider is in Windows forms.
Create trackBar1 ,and maximum = 100 points.
This is for Windows forms application:
On trackBar1_mouse_up:
if(trackBar1.Value < 100)
{
//Animation - slide back dynamicaly.
for(int i=0;i<=trackBar1.Value;i++){
int secondVal=trackBar1.Value-i;
trackBar1.Value=secondVal;
System.Threading.Thread.Sleep(15);
}
}
if(trackBar1.Value==100)
{
//sets enabled to false, then after load cannot move it.
trackBar1.Enabled=false;
MessageBox.Show("Done!");
}
And this for WPF Slider: (on PreviewMouseUp)
if (Convert.ToInt16(slider1.Value) < 99)
{
//Animation - slide back dynamicaly.
for (int i = 0; i < Convert.ToInt16(slider1.Value); i++)
{
int secondVal = Convert.ToInt32(slider1.Value) - i;
slider1.Value = secondVal;
System.Threading.Thread.Sleep(10);
if (secondVal < 2)
{
slider1.Value = 0;
}
}
}
if (Convert.ToInt16(slider1.Value) > 99)
{
//sets enabled to false, then after load cannot move it.
slider1.IsEnabled = false;
slider1.Value = 100;
MessageBox.Show("Done!");
}
Good luck! I hope it helps, try it with slider, but I think application will crash.