UWP UserControle Binding - dependency-injection

<ItemsControl x:Name="Template1" ItemsSource="{Binding RecentProjects}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<usercontrole:BtnWithBluring BtnContent="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I have the ItemControle and the UserControle inside like DataTemplate.
public Project BtnContent // Project is my own class
{
get { return (Project)GetValue(BtnContentProperty); }
set { SetValue(BtnContentProperty, value); }
}
// Using a DependencyProperty as the backing store for BtnContent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BtnContentProperty =
DependencyProperty.Register("BtnContent", typeof(Project), typeof(BtnWithBluring), null);
public BtnWithBluring()
{
this.InitializeComponent();
}
I have this in my UserControle.cs, but when I use breakpoint get and set aren't working
and I don't know why. Help me, please.

Related

Xamarin Forms: Blank space is showing on top of Listviews in ios platform

On top of my all listviews in a project, there is a blank space on the ios platform, no such issue on android or windows.
Screenshot:
My code:
<ListView
x:Name="MyItems"
RefreshCommand="{Binding RefreshCommand}"
IsPullToRefreshEnabled="True"
IsRefreshing="{Binding IsRefreshing}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout
Orientation="Vertical">
<StackLayout
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Orientation="Horizontal">
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<Label/>
</ListView.Footer>
</ListView>
Additional Details:
XF version: 4.8.0.1821
Project type is Portable
Please see this thread : https://developer.apple.com/forums/thread/683980.
To solve it we can add a custom renderer for ListView
Try the following code :
[assembly: ExportRenderer(typeof(ListView), typeof(LVRenderer))]
namespace YourNameSpace.iOS
{
public class LVRenderer : ListViewRenderer
{
public LVRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.SectionHeaderTopPadding = new nfloat(0);
}
}
}
}
Found it here :
Weird space on top of the listview after updating iOS to 15
The current accepted answer breaks on iOS 14.7.1, it creates the error "UIKit_UITableView_set_SectionHeaderTopPadding_System_nfloat - unrecognized selector sent to instance"
Here is a modified version that will work.
[assembly: ExportRenderer(typeof(ListView), typeof(LVRenderer))]
namespace YourNameSpace.iOS
{
public class LVRenderer : ListViewRenderer
{
public LVRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if (Control != null && UIDevice.CurrentDevice.CheckSystemVersion(15, 0))
{
Control.SectionHeaderTopPadding = new nfloat(0);
}
}
}
}
See why on this related issue: Value of type 'UITableView' has no member 'sectionHeaderTopPadding'
Also see the developer docs for sectionHeaderTopPadding which is denoted as iOS 15.0+: https://developer.apple.com/documentation/uikit/uitableview/3750914-sectionheadertoppadding

Update TextBox bound to DataGrid

I hope I can explain my problem properly.
I have a datagrid bound to an ObservableCollection object, and a TextBox bound to the selected item of my datagrid.
When I programmatically modify the SelectedItem property value (Name), my TextBox text value is not updated !
here is my design code:
<DataGrid Name="grid" HorizontalAlignment="Left" Margin="119,28,0,0" VerticalAlignment="Top" Height="237" Width="200" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="nom" Binding="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataGrid.Columns>
</DataGrid>
<TextBox Name="textbox" Text="{Binding ElementName=grid, Path=SelectedItem.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="18" Margin="119,276,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="200"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="392,54,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
and here is my Code Behind:
ObservableCollection<Element> obs;
class Element
{
public string Name { get; set; }
public Element(string name) { Name = name; }
}
public MainWindow()
{
InitializeComponent();
obs = new ObservableCollection<Element>() { new Element("element2"), new Element("element2"), new Element("element3")};
grid.ItemsSource = obs;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var sel = grid.SelectedItem as Element;
sel.Name = "something";
grid.Items.Refresh(); //this updates the selected element to "something" but does nothing to the textbox
}
Problem solved.
I had to fire PropertyChanged event whenever a modification is made to the model.
here my new code:
class Element : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
public Element(string name) { Name = name; }
}

Xamarin Forms ViewCell Swipe to Show Buttons

tl;dr: How do I use swipe to show buttons in Xamarin Forms like the iOS mail app
I am trying to implement swipe to show buttons for a Xamarin Forms iOS app similar to the UI of the iOS mail app or this https://components.xamarin.com/view/swtableviewcell. That component among many other examples I found look great for iOS native implementations but I need to show this UI via Xamarin forms.
Currently I have a custom swipe gesture recognizer like this:
[assembly: ExportRenderer(typeof(SwipeViewCell), typeof(SwipeIosRenderer))]
namespace MyApp.iOS.Renderers
{
public class SwipeIosRenderer : ViewCellRenderer
{
UISwipeGestureRecognizer swipeRightGestureRecognizer;
UISwipeGestureRecognizer swipeLeftGestureRecognizer;
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
swipeRightGestureRecognizer = new UISwipeGestureRecognizer(() => UpdateRight()) { Direction = UISwipeGestureRecognizerDirection.Right };
swipeLeftGestureRecognizer = new UISwipeGestureRecognizer(() => UpdateLeft()) { Direction = UISwipeGestureRecognizerDirection.Left };
if (e.NewElement == null)
{
if (swipeRightGestureRecognizer != null)
{
this.RemoveGestureRecognizer(swipeRightGestureRecognizer);
}
if (swipeLeftGestureRecognizer != null)
{
this.RemoveGestureRecognizer(swipeLeftGestureRecognizer);
}
}
if (e.OldElement == null)
{
this.AddGestureRecognizer(swipeRightGestureRecognizer);
this.AddGestureRecognizer(swipeLeftGestureRecognizer);
}
}
private void UpdateLeft()
{
Console.WriteLine("Left swipe");
}
private void UpdateRight()
{
Console.WriteLine("Right swipe");
}
}
That is bound to viewcells in a list. Now that I can recognize the "swipe" gesture I need help on how to actually move the view cell over and show a button like the examples I gave above?
It would be great to able to do this within the views XAML but am open to anything. I have a UpdateLeft and UpdateRight function that gets called on the respective swipe motions too if that can be used?
**EDIT: I need to do this for both left AND right swipe. ContextActions only provide the left swipe functionality.
Hope that makes sense!
Would Context Actions work for you? I haven't tried on other platforms, but on iOS it will create a swipe menu just like the Mail app. You should be able to use XAML and bind to command properties as well.
Edit:
Since you clarified that you need the left and right side swipe buttons that do not exist in the ContextActions, you could utilize the existing SWTableViewCell component that already has the desired behavior and adapt it to Xamarin.Forms.
iOSRenderer:
public class SwipeIosRenderer : TextCellRenderer
{
static NSString rid = new NSString("SWTableViewCell");
public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
{
var forms_cell = (SwipeCell)item;
SWTableViewCell native_cell = reusableCell as SWTableViewCell;
if (native_cell == null)
{
native_cell = new SWTableViewCell(UITableViewCellStyle.Default, rid);
if (forms_cell != null)
{
var cellDelegate = new CellDelegate(forms_cell);
native_cell.Delegate = cellDelegate;
if (forms_cell.LeftContextActions != null)
{
var left = new NSMutableArray();
foreach (var btn in forms_cell.LeftContextActions)
{
AddButton(left, btn);
}
native_cell.LeftUtilityButtons = NSArray.FromArray<UIButton>(left);
}
if (forms_cell.RightContextActions != null)
{
var right = new NSMutableArray();
foreach (var btn in forms_cell.RightContextActions)
{
AddButton(right, btn);
}
native_cell.RightUtilityButtons = NSArray.FromArray<UIButton>(right);
}
}
native_cell.TextLabel.Text = forms_cell.Text;
}
var fs = forms_cell.ImageSource as FileImageSource;
if (fs != null)
{
native_cell.ImageView.Image = UIImage.FromBundle(fs.File);
}
return native_cell;
}
void AddButton(NSMutableArray array,Button btn){
if (!String.IsNullOrEmpty(btn.Image?.File))
{
array.AddUtilityButton(btn.BorderColor.ToUIColor(), UIImage.FromBundle(btn.Image.File));
}
else
{
array.AddUtilityButton(btn.BorderColor.ToUIColor(), btn.Text);
}
}
public class CellDelegate : SWTableViewCellDelegate
{
SwipeCell forms_cell;
public CellDelegate(SwipeCell forms_cell)
{
this.forms_cell = forms_cell;
}
public override void DidTriggerLeftUtilityButton(SWTableViewCell cell, nint index)
{
if (forms_cell.LeftContextActions.Count > index)
{
var c = forms_cell.LeftContextActions[(int)index];
var cmd = c.Command;
if (cmd != null)
{
cmd.Execute(c.CommandParameter);
}
}
}
public override void DidTriggerRightUtilityButton(SWTableViewCell cell, nint index)
{
if (forms_cell.RightContextActions.Count > index)
{
var c = forms_cell.RightContextActions[(int)index];
var cmd = c.Command;
if (cmd != null)
{
cmd.Execute(c.CommandParameter);
}
}
}
}
Example XAML:
<ListView x:Name="SwipeList">
<ListView.ItemTemplate>
<DataTemplate>
<test:SwipeCell Text="{Binding Data}" ImageSource="{Binding Image}">
<test:SwipeViewCell.LeftContextActions>
<Button Text="L1" Command="{Binding LeftAction}" BorderColor="Aqua"/>
<Button Command="{Binding LeftAction2}" BorderColor="Gray" Image="xamarin.png"/>
</test:SwipeViewCell.LeftContextActions>
<test:SwipeViewCell.RightContextActions>
<Button Text="R1" Command="{Binding RightAction}" BorderColor="Blue" />
<Button Text="R2" Command="{Binding RightAction2}" BorderColor="Purple" />
</test:SwipeViewCell.RightContextActions>
</test:SwipeViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Example Code Behind
public class MyListItem
{
Page page;
public MyListItem(Page page)
{
this.page = page;
this.LeftAction= new Command(() => this.page.DisplayAlert("Left 1", this.Data, "OK"));
this.LeftAction2= new Command(() => this.page.DisplayAlert("Left 2", this.Data, "OK"));
this.RightAction= new Command(() => this.page.DisplayAlert("Right 1", this.Data, "OK"));
this.RightAction2= new Command(() => this.page.DisplayAlert("Right 2", this.Data, "OK"));
}
public string Image{ get; set; }
string data;
public string Data
{
get
{
return data;
}
set
{
data = value;
}
}
ICommand leftAction;
public ICommand LeftAction
{
get
{
return leftAction;
}
set
{
leftAction = value;
}
}
ICommand leftAction2;
public ICommand LeftAction2
{
get
{
return leftAction2;
}
set
{
leftAction2 = value;
}
}
ICommand rightAction;
public ICommand RightAction
{
get
{
return rightAction;
}
set
{
rightAction = value;
}
}
ICommand rightAction2;
public ICommand RightAction2
{
get
{
return rightAction2;
}
set
{
rightAction2 = value;
}
}
public override string ToString()
{
return this.Data;
}
}
public TestPage()
{
InitializeComponent();
this.SwipeList.ItemsSource = new List<MyListItem>(){
new MyListItem(this){Data="A"},
new MyListItem(this){Data="B", Image="xamarin.png"},
new MyListItem(this){Data="C"},
new MyListItem(this){Data="D"},
};
}
Context Actions wasn't exactly what my client wanted. The row menu didn't appear on swipe. It appeared when they hold tap on the row, and the menu appeared at the top of the screen.
I was able to accomplish the swipe row behaviour with the new Xamarin.Forms
SwipeView
Pass the current row into the CommandParameter, and use it in the event handler.
FYI: For some reason the SwipeView has a default BackgroundColor of white, which you can override with something else to match your theme.
Xaml:
<ListView Margin="-20,0,0,0" x:Name="photosListView" ItemSelected="OnItemSelected" VerticalOptions="FillAndExpand" SeparatorColor="Gray" VerticalScrollBarVisibility="Default" HasUnevenRows="true" SeparatorVisibility="Default" Background="{StaticResource PrimaryDark}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<SwipeView BackgroundColor="{StaticResource PrimaryDark}" >
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem Text="Delete" BackgroundColor="LightPink" Clicked="OnDeleteRow" CommandParameter="{Binding .}" />
</SwipeItems>
</SwipeView.RightItems>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackLayout Orientation="Horizontal">
<CheckBox IsVisible="{Binding SelectEnabled}" Color="{StaticResource White}" IsChecked="{Binding Selected}" Margin="20,0,-15,0" CheckedChanged="OnItemCheckedChanged" />
<Grid WidthRequest="70" HeightRequest="50">
<Grid.Margin>
<OnPlatform x:TypeArguments="Thickness" Android="15,0,0,0" iOS="10,0,0,0" />
</Grid.Margin>
<Image Aspect="AspectFill" Source="{Binding ThumbImageSource}" HorizontalOptions="Fill" />
</Grid>
</StackLayout>
<StackLayout Grid.Column="1" Spacing="0" Padding="0" Margin="0,5,0,0">
<Label Text="{Binding Photo.Description}" TextColor="{StaticResource TextColour}" FontSize="16" FontAttributes="Bold" />
<Label Text="{Binding DateTakenString}" TextColor="{StaticResource TextColour}" FontSize="14" />
</StackLayout>
</Grid>
</SwipeView>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
cs:
public async void OnDeleteRow(object sender, EventArgs e)
{
if (await GetDeleteRowConfirmationFromUser())
{
SwipeItem si = sender as SwipeItem;
PhotoListItem itemToDelete = si.CommandParameter as PhotoListItem;
LocalDatabaseService db = new LocalDatabaseService();
db.DeletePhoto(itemToDelete.Photo);
_listItems.Remove(itemToDelete);
}
}

Binding between Data template and ListBox - Windows Phone

I'm having problems to do a binding in Windows Phone. Hope you can help me.
I have the following Data Template:
<DataTemplate>
<TextBox Name="txt1"/>
<TextBox Name="txt2"/>
</DataTemplate>
I have a ListBox that receives the following Class in the ItemsSource Property:
public class Product
{
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
Is there anyway to bind the Text property with the Object of the ListBoxItem like...
<TextBox Name="txt1" Text={Binding ElementName=ListBox, Path=SelectedItem.Product.Name}/>
I got a working example
xaml:
Here's the code
<Grid x:Name="gdTest" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,0,5,0" >
<ListBox Width="400" Margin="10" x:Name="lstDemo">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Id}" Margin="20" />
<TextBlock Text="{Binding Path=Name}" Margin="20"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using StackOverFlowTestApp.Resources;
using Microsoft.Phone.Tasks;
using Microsoft.Phone.UserData;
using Windows.UI;
using System.Windows.Media;
using System.IO;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework;
namespace StackOverFlowTestApp
{
public partial class MainPage : PhoneApplicationPage
{
private SoundEffect effect;
// Constructor
public MainPage()
{
InitializeComponent();
List<Product> liProd = new List<Product>();
for (int i = 0; i < 10; i++) {
liProd.Add(new Product()
{
Id = i,
Name = "Anobik" + i.ToString()
});
}
lstDemo.ItemsSource = liProd;
}
}
public class Product
{
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
}
if you require any more explanation then let me know.

UserControl Dependencyproperty not updating through binding

I'm creating a Windows 8 app, and I'm struggling the last couple of days with a custom user control. I can't really figure out whats wrong.
The weird thing is that the dependencyproperty calls the propertychanged event when I change Source in code, but with the binding its not updating.
So here's my code:
GamePage.xaml.cs
public sealed partial class GamePage
{
GamePageViewModel viewModel;
public GamePage()
{
this.InitializeComponent();
viewModel = new GamePageViewModel();
}
}
GamePage.xaml
<common:LayoutAwarePage x:Class="WordSearcher.GamePage"
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:common="using:WordSearcher.Common"
xmlns:controls="using:WordSearcher.Controls"
xmlns:ignore="http://www.ignore.com"
mc:Ignorable="d ignore"
d:DesignHeight="768"
d:DesignWidth="1366"
DataContext="{Binding GamePageViewModel, Source={StaticResource Locator}}">
<StackPanel Orientation="Horizontal">
<StackPanel.Background>
<ImageBrush ImageSource="Assets/Wood.jpg" Stretch="UniformToFill"/>
</StackPanel.Background>
<controls:PuzzleControl Source="{Binding Path=PuzzleData}"/>
</StackPanel>
</common:LayoutAwarePage>
GamePageViewModel.cs
public class GamePageViewModel : ViewModelBase
{
private List<string> _puzzleData;
public List<string> PuzzleData
{
get
{
return _puzzleData;
}
set
{
this._puzzleData = value;
RaisePropertyChanged("PuzzleData");
}
}
public GamePageViewModel()
{
SetNewData();
}
private async void SetNewData()
{
await SomeManager.Prepare();
PuzzleData = SomeManager.Create(20);
}
}
PuzzleControl.xaml.cs
<UserControl
x:Class="WordSearcher.Controls.PuzzleControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WordSearcher.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="500"
d:DesignWidth="500">
<Grid x:Name="puzzleGrid"
Width="500"
Height="500"
>
</Grid>
</UserControl>
PuzzleControl.xaml.cs
public sealed partial class PuzzleControl : UserControl
{
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(ObservableCollection<string>), typeof(PuzzleControl), new PropertyMetadata(null, PropertyChanged));
private static void PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//
// not called from binding
//
((PuzzleControl)d).OnItemsSourcePropertyChanged(e);
}
private void OnItemsSourcePropertyChanged(DependencyPropertyChangedEventArgs e)
{
Source = (ObservableCollection<string>)e.NewValue;
SetGridData();
}
public ObservableCollection<string> Source
{
get
{
return (ObservableCollection<string>)GetValue(SourceProperty);
}
set
{
SetValue(SourceProperty, value);
}
}
public PuzzleControl()
{
this.InitializeComponent();
CreateRowsAndColumns();
}
private void CreateRowsAndColumns()
{
//create rows and columns in puzzleGrid
//works fine
}
private void SetGridData()
{
//fill puzzleGrid with data
//works fine
}
}
Does anyone knows with is wrong in my code? Because when I put Source = new ObservableCollection(); in the constructor of PuzzleData, the PropertyChanged event will raise. Is it anything with the DataContext?
Thnx in advance!
I don't know for sure,
but you set <controls:PuzzleControl Source="{Binding Path=PuzzleData}"/>
PuzzleData = List<string>
and
Source = ObservableCollection<string>
If the binding even works the first time (what it apperantly does) then it might be the case the source is set to List<string> in some way instead of ObservableCollection<string>. That might be the case why your dependencyproperty method (PropertyChanged) is not called because it is registered to ObservableCollection<string>.
But this is all pure speculation, haven't tested it.
After I got his code an reviewed it I found out that the PuzzleData was never really set and that that was the error... False Alarm....
Are you sure binding context? And How binding object? If you use your user control like in a gridview, DataContext is changed, and differend Datacontext of root page.
<controls:PuzzleControl Source="{Binding Path=DataContext.PuzzleData}"/>
if you use sub control, your user control; bind ElementName property like this:
<controls:PuzzleControl Source="{Binding Path=DataContext.PuzzleData, ElementName=pageRoot}"/>
If you not sure, tracing DataContext binding values on debug via breakpoints.

Resources