I am trying to make a basic browser in XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBox x:Name="urlBox"
Text="{Binding ElementName=webView,Path=Source,Mode=TwoWay}"
KeyDown="OnUrlEnter" AcceptsReturn="False"
TextWrapping="Wrap"/>
<WebView Grid.Row="1" x:Name="webView"/>
</Grid>
What I want is that when a user click on a link in the WebView, the URL TextBox is updated accordingly and of course, if user enters the URL and press [Enter] button, the webView should navigate to the specified URL but I have already achieved that. I have tried handling NavigationStarting, NavigationCompleted, NavigationFailed (deprecated), LoadCompleted (deprecated) but none of them helps me get new URL whenever user click on links in the WebView. (The event handlers are only invoked once when I do webView->Navigate. Needless to say, {Binding ElementName=webView,Path=Source,Mode=TwoWay} does not seem to work.)
What should I do to synchronize the WebView's URL and the TextBox? There should be an easy way to do this.
Hi what you could use is.
expose a property
private string browserURL;
public string BrowserURL
{
get{return browserURL;}
set{ browserURL=value;
NotifyPropertyChanged("BrowserURL");
}
}
public constructor()
{
WebBrowser.Navigating += WebBrowser_Navigating;
this.DataContext= this;
}
private void TechMCaresBrowsercontroll_Navigating(object sender, NavigatingEventArgs e)
{
browserURL=e.Uri;
}
XAML Code
<TextBox x:Name="urlBox"
Text="{Binding BrowserURL}"
KeyDown="OnUrlEnter" AcceptsReturn="False"
TextWrapping="Wrap"/>
so every time the browser will be navigating to a new URL you exposed property's value would get changed calling NotifyPropertyChanged and would update your textbox.
Related
I have UWP page and it is using data binding to set content on xaml elements (texts, buttons, image). I am setting the content using async calls (getting content from internet). In the page's c'tor, I subscribed to Loaded event. And obtained button using FindName() method on the control. Got the content on the button and it is coming as empty. I am assuming it is because I am setting content using async calls and when I debugged using breakpoints, it is indeed the case. The content is set after Loaded event is fired.
So my question is - what is the event fired when content on xaml elements becomes available?
Below is my code:
void ValuePropControl::ControlLoaded(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e)
{
auto b{ sender.as<ValueProp::ValuePropControl>() };
auto buttttton = b.FindName(L"OpenFamilyAppButton");
auto bbttnn = buttttton.as<Windows::UI::Xaml::Controls::Button>();
auto contentt = bbttnn.Content();
auto texxxt = winrt::unbox_value<hstring>(contentt);
}
Variable 'texxxt' is empty.
<Grid
Grid.Column="0"
CornerRadius="3">
<Button
x:Name="OpenFamilyAppButton"
MinWidth="118"
MinHeight="30"
HorizontalAlignment="Left"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
AutomationProperties.Name="{x:Bind ViewModel.PrimaryActionAutomationName, Mode=OneWay}"
BorderThickness="1"
Click="{x:Bind ViewModel.InvokePrimaryAction}"
Content="{x:Bind ViewModel.PrimaryAction, Mode=OneWay}"
CornerRadius="3"
Style="{StaticResource AccentButtonStyle}" />
</Grid>
I'm working on a view (called 'Familify') which shows users a list of assets, and allows them to delete an asset from the list. The assets are stored in an ObservableCollection in the ViewModel, so the command to delete simply takes the asset object and removes it from collection. I'm having issues getting the 'delete' functionality working. Here is the XAML and codebehind:
Familify.xaml
<ListView
ItemsSource="{Binding Assets}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80px" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="150px" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60px" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Text="{Binding number}" FontFamily="Consolas"/>
<TextBlock
Grid.Column="1"
Text="{Binding type}"/>
<TextBlock
Grid.Column="2"
Text="add binding here"/>
<TextBlock
Grid.Column="3"
Text="add binding here"/>
<Button
Command="{x:Bind ViewModel.RemoveAssetCommand}"
CommandParameter="{Binding}"
Content=""
FontFamily="Segoe MDL2 Assets"
Grid.Column="4">
</Button>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Familify.xaml.cs
namespace asset_manager.Views
{
public sealed partial class Familify : UserControl
{
FamilifyViewModel ViewModel { get; set; }
public Familify()
{
this.InitializeComponent();
DataContextChanged += (s, e) =>
{
ViewModel = DataContext as FamilifyViewModel;
};
}
}
}
The idea is that clicking the button removes the asset from the list. (Just to note, the normal binding showing number, type, etc. is working correctly.) My thinking so far:
Try to use binding to access the RemoveAssetCommand stored in the View Model for the page. However, I couldn't get ancestral binding to work (i.e. trying to find the data context of an element higher up in the XAML hierarchy didn't work because findAncestor isn't a thing in UWP.)
x:Bind looked like a good solution, because it uses an explicit path to the property. So, if I declared ViewModel in my code behind, I could use x:Bind ViewModel.property. All well and good. I did just that, and intellisense allowed me to access the ViewModel.RemoveAssetCommand when typing it out.
However, this did not work, because I get the error no DataType defined for DataTemplate. This makes sense, so I tried two things.
x:DataType="Models:Asset" (put in the DataTemplate tag above) is the model being shown in the data template, so I tried that first. Of course, the command is not declared in the model, it's declared in the View Model, so that didn't work.
I instead tried x:DataType="ViewModels:FamilifyViewModel", thinking I could just use x:Bind with that. However, I then got an error that it couldn't cast an object of type Asset to FamilifyViewModel. This makes sense, because the object getting passed to this data template is of the type Asset.
This is a pain, because the whole reason I thought x:Bind would work is that I could just access the property directly from the ViewModel in the codebehind.
Explicitly stated, 1) is it possible to use x:Bind within a data template to access a base level property (in this case, a Prism command) on the ViewModel? and 2) is there a better way to go about implementing this functionality?
Is it possible to use x:Bind within a data template to access a base level property (in this case, a Prism command) on the ViewModel?
Yes, if you want to access a base level, you can reassign DataContext of button like following:
<Button DataContext="{Binding ElementName=Familily, Path=DataContext}"/>
The Family is the name of UserControl.
is there a better way to go about implementing this functionality?
When you put commad in the ViewModel and bind the button as above. The the bind item of button will become Family DataContext. So you could not invoke delete action directly in the ViewModel.
The best practice to implement this functionality is that put the RemoveAssetCommand in the Asset class. And use the ItemsSource of ListView as Button CommandParameter.
<Button
Command="{Binding RemoveAssetCommand}"
CommandParameter="{Binding ElementName=MyListView, Path=ItemsSource}"
Content=""
FontFamily="Segoe MDL2 Assets"
Grid.Column="4">
</Button>
Asset.cs
public class Asset
{
public string number { get; set; }
public string type { get; set; }
public ICommand RemoveAssetCommand
{
get
{
return new CommandHandler<ObservableCollection<Asset>>((item) => this.RemoveAction(item));
}
}
private void RemoveAction(ObservableCollection<Asset> items)
{
items.Remove(this);
}
}
ViewModel.cs
public class FamilifyViewModel
{
public ObservableCollection<Asset> Assets = new ObservableCollection<Asset>();
public FamilifyViewModel()
{
Assets.Add(new Asset { number = "100001", type = "hello" });
Assets.Add(new Asset { number = "100001", type = "hello" });
Assets.Add(new Asset { number = "100001", type = "hello" });
Assets.Add(new Asset { number = "100001", type = "hello" });
}
}
I'm trying to have a login page for my app, jus like the windows login account. But this is not a account sign in. Once the user sets a password, every time the user opens the app afresh, it asks for the password.
From this site, I got how to create the login page. But the problem im facing is, once i put the grid inside the ContentControl it does not expand.
I had to specify the height and width of the grid named mainbackground. I do not want to hard code the values because as the resolution changes the height and width may vary.
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush }">
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ContentControl x:Name="parent" Grid.Row="0" Grid.RowSpan="1">
<Grid x:Name="MainBackground" Height="768" Width="1366">
<!-- rest of the xaml declaration-->
</Grid>
</ContentControl>
<ContentControl x:Name="container" Height="450" Margin="0,194,0,124">
<Popup x:Name="logincontrol1" IsOpen="False" >
</ContentControl>
</Grid>
Few questions,
Is this the correct method to create login page?
Why doesn't the grid expand to the who screen without me having to specify the height and width?
What is the actual use of content control? Didnt find elaborate explanations online.
Thank you
The advice is not to write your own login page, but to use the CredentialPicker control.
In your case, you are not hooking the picker into anything, and that is a valid scenario. Set the picker options up like this...
CredentialPickerOptions opts = new CredentialPickerOptions {
AuthenticationProtocol = AuthenticationProtocol.Basic,
Caption = "My App Login",
Message = "Log in here",
TargetName = "MyApp"
};
var res = await CredentialPicker.PickAsync(opts);
and then you can access the CredentialUserName and CredentialPassword values for your own logic.
I want to implement auto-complete on a textbox in a Windows 8 UI / Metro UI app using C#/XAML.
At the moment, when the soft / touch keyboard shows, it obscures the auto-complete box. However, on the text box focus, Windows 8 automatically scrolls the entire view up and ensures the text box is in focus.
In reality, all I want is the view to scroll up a little more (in fact, by the height of the auto-complete box).
I realise I can intercept the Showing event of InputPane.GetForCurrentView()
I can set InputPaneVisibilityEventArgs.EnsuredFocusedElementInView to true inside the Showing event fine (so Windows won't try to do anything).... however, how can I invoke the same scrolling functionality that Windows 8 would do, but ask it to scroll a little more!?
Here's the code for the main page:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,200,0,0">
<TextBlock HorizontalAlignment="Center" FontSize="60">App 1</TextBlock>
<TextBlock HorizontalAlignment="Center">Enter text below</TextBlock>
<TextBox HorizontalAlignment="Center" Margin="-10,0,10,0" Width="400" Height="30"/>
<ListBox HorizontalAlignment="Center" Width="400">
<ListBoxItem>Auto complete item 1</ListBoxItem>
<ListBoxItem>Auto complete item 2</ListBoxItem>
<ListBoxItem>Auto complete item 3</ListBoxItem>
<ListBoxItem>Auto complete item 4</ListBoxItem>
<ListBoxItem>Auto complete item 5</ListBoxItem>
</ListBox>
</StackPanel>
</Grid>
If you start up the simulator with the lowest resolution, use the hand to "touch" the textbox, this will bring up the soft keyboard. In the real app, the auto complete list will appear with items as the user enters text.
So in a nutshell, how can I move the screen up a bit more so the user can see the entire autocomplete list?
Bear in mind, in the real app, it'll be worse, as the user may not even notice the autocomplete list appearing "underneath" the keyboard.
I really would appreciate some advice, many thanks!
I have created an AutoCompleteBox for Windows Store apps, the nuget package is available at https://nuget.org/packages/AutoCompleteBoxWinRT
Ok, here is how I would tackle this since I cannot seem to find any way to control the scrolling of the app based on the appearance of the keyboard. I would create a user control that would form the basis for the auto-complete textbox.
<UserControl
x:Class="App6.MyUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App6"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<TextBox x:Name="textBox" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" GotFocus="textBox_GotFocus" LostFocus="textBox_LostFocus" />
<ListBox x:Name="listBox" Height="150" Margin="0,-150,0,0" VerticalAlignment="Top" Visibility="Collapsed"/>
</Grid>
This is an incredibly basic implementation, so you will have to tweak to meet your needs.
Then, I would add the following code-behind to the user control
public sealed partial class MyUserControl1 : UserControl
{
// Rect occludedRect;
bool hasFocus = false;
public MyUserControl1()
{
this.InitializeComponent();
InputPane.GetForCurrentView().Showing += MyUserControl1_Showing;
}
void MyUserControl1_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
{
if (hasFocus)
{
var occludedRect = args.OccludedRect;
var element = textBox.TransformToVisual(null);
var point = element.TransformPoint(new Point(0, 0));
if (occludedRect.Top < point.Y + textBox.ActualHeight + listBox.ActualHeight)
{
listBox.Margin = new Thickness(0, -listBox.ActualHeight, 0, 0); // Draw above
}
else
{
listBox.Margin = new Thickness(0, textBox.ActualHeight, 0, 0); // draw below
}
}
}
private void textBox_GotFocus(object sender, RoutedEventArgs e)
{
listBox.Visibility = Windows.UI.Xaml.Visibility.Visible;
hasFocus = true;
}
private void textBox_LostFocus(object sender, RoutedEventArgs e)
{
listBox.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
hasFocus = false;
}
}
Next steps would be to expose properties to pass data to be bound to the ListBox. Hard core would be ListBoxItem templating and more, depending on how reusable you wanted it to be.
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.