I have a user control and a pivot on the same page.
I would like the user control to show something different based on the selected tab in the pivot.
At first, I thought that I could pass the pivot.SelectedIndex to the user control as a dependency property like so:
// Pivot property
public static readonly DependencyProperty SelectedTabProperty = DependencyProperty.Register
(
"SelectedTab",
typeof(int),
typeof(GaugeControl),
new PropertyMetadata(-1)
);
public int SelectedTab
{
get {
return (int)GetValue(SelectedTabProperty); }
set
{
SetValue(SelectedTabProperty, value);
}
}
With xaml in the parent page:
<local:GaugeControl
Data="{x:Bind data_collector}"
SelectedTab="{Binding ElementName=MainPivot, Path=SelectedIndex}"
Margin="0,0,0,100"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<Pivot x:Name="MainPivot"
HorizontalAlignment="Center"
Margin="0,0,0,0"
Style="{StaticResource PivotStyleBottomHeader}">
....
However, this code generates an error The application called an interface that was marshalled for a different thread in the getter.
Is there a better way to do this?
Related
I have DependecyProperty (named Block) on a XAML UserControl (named BlockPresenter) which expects a data type of IBlock. This IBlock intefae has a Visual GetControl() method - amongst other things - which returns an element of some kind to allow the user to edit the values for that specific block. The BlockPresenter provides a way of choosing which block to use (e.g. BlockA, BlockB, etc.) and also shows the GetControl return valued for the block itself.
There are models which contain one or more IBlock properties under varying different names. Each of these models has their own user control which has the model as it's DataContext. In the XAML for the model's control, I have lines such as <controls:BlockPresenter Block="{Binding Foo}" /> to show the control and allow the user to change the block type or the properties for their chosen block.
When the user changes block type from the dropdown in BlockPresenter, a new instance of the selected block is created and set as the Block DependencyProperty and the presenter calls GetControl for the new Block. This is fine, however the problem is that this does not send the new value up to the model.
BlockPresenter.xaml
<Border BorderThickness="2" BorderBrush="#F00">
<StackPanel>
<ComboBox x:Name="BlockSelection" SelectedValuePath="BlockType" DisplayMemberPath="Name" SelectionChanged="BlockSelection_SelectionChanged" />
<ContentControl x:Name="ContentContainer" Margin="0" />
</StackPanel>
</Border>
BlockPresenter.xaml.cs
public BlockPresenter() {
InitializeComponent();
BlockSelection.ItemsSource = new List<BlockListItem> {
new BlockListItem("BlockA", typeof(BlockA)),
new BlockListItem("BlockB", typeof(BlockB)),
// BlockListItem is a simple class containing just "Name" and "BlockType" as read-only auto properties.
};
}
private static void OnBlockChange(DependencyObject conditionPresenter, DependencyPropertyChangedEventArgs eventArgs) {
var control = (BlockPresenter)conditionPresenter;
var newBlock = (IBlock)eventArgs.NewValue;
control.ContentContainer.Content = newBlock?.GetControl();
control.BlockSelection.SelectedValue = eventArgs.NewValue?.GetType();
}
public static readonly DependencyProperty BlockProperty = DependencyProperty.Register("Block", typeof(IBlock), typeof(BlockPresenter), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, OnBlockChange));
public IBlock Block {
get => (IBlock)GetValue(BlockProperty);
set => SetValue(BlockProperty, value);
}
private void BlockSelection_SelectionChanged(object sender, SelectionChangedEventArgs e) {
Block = (IBlock)Activator.CreateInstance(BlockSelection.SelectedValue);
}
DefaultModelControl.xaml
<Grid>
<Grid.ColumnDefinitions>
...
</Grid.ColumnDefitions>
<Label Content="Main Block:" Grid.Row="0" />
<logic:BlockPresenter Block="{Binding MainBlock}" Grid.Row="0" Grid.Column="1" />
<Label Content="Subblock:" Grid.Row="1" />
<logic:BlockPresenter Block="{Binding SubBlock}" Grid.Row="1" Grid.Column="1" />
</Grid>
When the main or sub block types are changed for the default model, the value inside the model (MainBlock or SubBlock properties) are not updated to reflect the newly selected block.
I know one solution could be to raise a custom event (e.g. BlockChanged) from the BlockPresenter when a new value is set, passing this value as event args and then capturing this event in the DefaultModelControl.xaml.cs file and setting the property to the new reference.
I was wondering if there is another way of doing this without the listeners though, since it's annoying to have to create them for each block presenter I want.
Am I going about this wrong? Would love to hear peoples constructive thoughts on this. Thanks :)
I'm doing a binding in UWP, the listview is not showing(so it's working fine), but the header is still on the view.
So the problem is that the header is not getting the binding from the PivotItem, what can be the problem?
<PivotItem Header="Hello" Visibility="{Binding isVisible, Converter={StaticResource Visibility}}">
<ListView ItemsSource="{Binding myList}"/>
</PivotItem>
This is actually a bit tricky. Setting Visibility on a PivotItem does indeed hide only the contents of the item, not the PivotItem itself. That said, you can hide it from the code-behind by removing it from the pivot completely:
MyPivot.Items.Remove(HideablePivotItem);
The problem is now the fact that you need to trigger it on binding change. For that purpose I suggest you use a custom Behavior and a CallMethodAction.
First install the Microsoft.Xaml.Behaviors.Uwp.Managed from NuGet (right-click your project, click Manage NuGet Packages... find the package using search and click Install.
Now, create a new class DataChangeTriggerBehavior class:
public class DataChangeTriggerBehavior : Trigger<FrameworkElement>
{
public static readonly DependencyProperty BindingProperty = DependencyProperty.Register(
nameof(Binding), typeof(object), typeof(DataChangeTriggerBehavior), new PropertyMetadata(null, BindingChanged));
public object Binding
{
get => (object)GetValue(BindingProperty);
set => SetValue(BindingProperty, value);
}
private static void BindingChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
DataChangeTriggerBehavior changeTrigger = (DataChangeTriggerBehavior)dependencyObject;
if (changeTrigger.AssociatedObject == null) return;
Interaction.ExecuteActions(changeTrigger.AssociatedObject, changeTrigger.Actions, args);
}
}
This behavior will observe a binding and trigger its associated actions whenever the binding changes.
Now, update your Page element as follows:
<Page
...
x:Name="Page"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:customBehavior="using:XXX"
mc:Ignorable="d">
Where XXX is the namespace where your behavior is defined.
Now use the behavior in your Pivot:
<Pivot x:Name="MyPivot">
<interactivity:Interaction.Behaviors>
<local:DataChangeTriggerBehavior Binding="{Binding isVisible}">
<core:CallMethodAction MethodName="TogglePivotItem"
TargetObject="{Binding ElementName=Page}" />
</local:DataChangeTriggerBehavior>
</interactivity:Interaction.Behaviors>
<PivotItem Header="Hello" Visibility="Collapsed" x:Name="HideablePivotItem">
<ListView ItemsSource="{Binding myList}"/>
</PivotItem>
</Pivot>
Finally you must define the TogglePivotItem method in your page's code-behind:
private int originalPosition = 0;
public void TogglePivotItem()
{
if (MyPivot.Items.Contains(HideablePivotItem))
{
//store the position of the item to be readded later
originalPosition = MyPivot.Items.IndexOf(HideablePivotItem);
MyPivot.Items.Remove(HideablePivotItem);
}
else
{
MyPivot.Items.Insert(originalPosition, HideablePivotItem);
}
}
I am storing the original position of the PivotItem so that it can be re-added to the same place again.
I am using Caliburn.Micro in a WPF project with c#.
I wanted a way, like in android to find a "TextBlock" in a xaml view by it's "id" so that I can manipulate its properties.
I was thinking of doing something like this but for c#:
TextBlock textblock = (TextBlock ) myView.findViewById(R.id.myTextBlock);
so I can collapse and make it visible again.
<TextBlock x:Name="MyTextBlockId"
Text="Incorrect user credentials. Forgot password, click here"
Visibility="Collapsed"/>
MVVM Approach
ViewModel
class MyViewModel : PropertyChangedBase
{
private bool _isBadLogin;
public bool IsBadLogin
{
get => _isBadLogin;
set => Set(ref _isBadLogin, value);
}
}
XAML
<TextBlock x:Name="MyTextBlockId"
Text="Incorrect user credentials. Forgot password, click here"
Visibility="{Binding IsBadLogin, Converter={StaticResource BooleanToVisibilityConverter}"/>
Code in my MainPage.xaml
<TextBox x:Name="TextTextBox"
Text="{Binding Path=Text, Mode=TwoWay}"
TextWrapping="Wrap" />
.
.
.
<!--Button with some Command-->
<Button x:Name="SearchButton"
Content="Search"
HorizontalAlignment="Center"
Command="{Binding Command}"/>
Code in my ViewModel (property that is binded with TextBox)
public string Text
{
get
{
return ssModel.Text;
}
set
{
ssModel.Text = value;
OnPropertyChanged();
}
}
When you press SearchButton, is performing Command which return List of ints (this ints are indexes to coloring in TextBox).
For example I've Text:
LoremIpsumDolorSitAmet
Then I press SearchButton, Command return me for example list of three numbers {2, 5, 13}. Now I want to coloring TextTextBox characters on this positions so, I want to get something like:
And this is exactly what I want to get. Coloring text in TextTextBox on the specified possitions.
I change TextBox to RichEditBox and write DependencyProperty to bind control with property in View Model. This is class with DependencyProperty:
public class RichTextC : DependencyObject
{
public static string GetRichText(DependencyObject obj)
{
return (string)obj.GetValue(RichTextProperty);
}
public static void SetRichText(DependencyObject obj, string value)
{
obj.SetValue(RichTextProperty, value);
}
public static readonly DependencyProperty RichTextProperty =
DependencyProperty.Register("RichText", typeof(string), typeof(RichTextC), new PropertyMetadata(string.Empty, callback));
private static void callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var reb = (RichEditBox)d;
reb.Document.SetText(TextSetOptions.FormatRtf, (string)e.NewValue);
}
}
And RichEditBox at MainPage.xaml:
<RichEditBox local:RichTextC.RichText="{Binding MyRichText, Mode=TwoWay}"
Margin="0,30,0,0""/>
But I have a another problem, because this DependencyProperty bind only in one way. When I set text/content of property at View Model control RichEditBox at MainPage will be notify and show new text. But when I change content of RichEditBox at MainPage it does not notify property in View Model - so binding from View to View Model does not work.
The TextBox doesn't support multi-coloured text. If you want editable coloured text you'll need to use the RichEditBox instead.
However, there isn't a direct way to bind to the text of a RichEditBox. You can set the text and its character formatting programatically through the RichEditBox's ITextDocument interfaces. For example, the following will set position 2 to Red. You can loop through your list of ints to set all of its ranges before calling ApplyDisplayUpdates:
ITextDocument doc = rtb.Document;
ITextRange range = doc.GetRange(2,3);
range.CharacterFormat.ForegroundColor = Windows.UI.Colors.Red;
rtb.Document.ApplyDisplayUpdates();
The other possibility would be to create a string with RTF codes in it and set that with ITextDocument.SetText.
If you want to bind the Text you can create an attached property which accepts the RTF and calls SetText or which accepts your own simpler mark-up script and calls ITextRange.CharacterFormat.ForegroundColor. Either way it would be similar to what I demonstrated to bind HTML to a WebView in my blog entry Binding HTML to a WebView with Attached Properties
I created a UserControl that has a ContentControl in it. This ContentControl gets Buttons from the normal .xaml-pages. But depending on some events I need to change this Button's Label or Image but i am getting a NullReferenceException.
UserControl1.xaml
<Grid>
<!-- different Stuff that needs to be around -->
<ContentControl Content="{Binding UserControlContent, ElementName=userContent}"/>
</Grid>
UserControl1.xaml.cs
public static readonly DependencyProperty AppBarContentProperty =
DependencyProperty.Register("UserControlContent", typeof(Grid), typeof(UserControl1), new PropertyMetadata(new Grid()));
public Grid UserControlContent
{
get { return (Grid)GetValue(UserControlContentProperty); }
set { SetValue(UserControlContentProperty, value); }
}
MainPage.xaml
<local:UserControl1>
<local:UserControl1.UserControlContent>
<Grid>
<Controls:RoundButton x:Name="btn1"/>
</Grid>
</local:UserControl1.UserControlContent>
</local:UserControl1>
MainPage.xaml.cs
MainPage()
{
btn1.Label = "new label";
}
As soon as I try this with a button inside of the UserControl it fails. With buttons that stay outside it works.
Is there any deeper binding possible to keep control of these buttons?
The trick is using the mvvm-binding!
The button's values are bound now:
Label="{Binding RoundButtons[3].Label}"
Visibility="{Binding RoundButtons[3].VisibilityState, FallbackValue=Visible}"
This allows me to define default-values and still change them on the fly as I need them to be changed.
Hope someone needs this information ;)