I have the problem that I would like to render the contents of a variable (referenced via the expression "Metatag.Configuration[VisualizerCode].Value" in the code below). The variable contains xaml code (as a string), for example the following contents:
<Grid>
<Canvas>
<Border Canvas.Top="0" Canvas.Left="390" Width="50" Height="100" BorderThickness="2" BorderBrush="Black"> </Border>
<Border Canvas.Top="100" Canvas.Left="340" Width="100" Height="50" BorderThickness="2" BorderBrush="Black"> </Border>
</Canvas>
</Grid>
In my application I have a Grid, in which I would like to render the contents of the variable:
<Grid Margin="0,10,0,0" Visibility="Visible">
<ContentControl Content="{Binding Path=Metatag.Configuration[VisualizerCode].Value}">
</ContentControl>
Unfortunately, if I run this, the string (= uninterpreted contents of the variable) is printed as text in the Grid, instead of being interpreted (in which case 2 nice, simple borders should be drawn).
How can I get XAML to interpret the contents of the variable and render it ?
Thanks !
Woelund
You can try using some custom Converter to convert (parse) the string to some instance of the Grid:
public class StringToElementConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture){
var pc = new ParserContext();
pc.XmlnsDictionary[""] =
"http://schemas.microsoft.com/winfx/2006/xaml/presentation";
pc.XmlnsDictionary["x"] = "http://schemas.microsoft.com/winfx/2006/xaml";
return XamlReader.Parse(System.Convert.ToString(value), pc);
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture){
throw new NotImplementedException();
}
}
Declare the converter as some resource and used for the Binding in XAML code:
<Window.Resources>
<local:StringToElementConverter x:Key="elementConverter"/>
</Window.Resources>
<ContentControl Content="{Binding Metatag.Configuration[VisualizerCode].Value,
Converter={StaticResource elementConverter}}"/>
I hope you know how to declare the prefix local representing the local namespace in which your converter class is declared.
Related
I have a big lists of objects which are binded to a ListBox. In that list box i am displaying the name of a file and a byte array of an image which is coming form WCF. I would display the files in the listbox depending on the scroll postion, so the file which are not visible from that scroll position to be set as Hidden.
I found on the internet a way which might help me but i cannot see a way that this will work.
I found convertors which should help me, and set up that class as a window resource. When I am opening the app all the items are converted to be visible.
This is the class:
class NullVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? Visibility.Hidden : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? Visibility.Visible : Visibility.Hidden;
}
}
In Xaml i setup this class as a window Resource and i binded the functionality to the image and name in the Listbox items
<Window.Resources>
<view:NullVisibilityConverter x:Key="NullToVis"/>
</Window.Resources>
<Image Source="{Binding Path=Image}" Visibility="{Binding Path=Image, Converter={StaticResource NullToVis}}" Height="80" Width="80" Grid.Column="0"/>
<StackPanel Grid.Column="1" Visibility="{Binding Path=Image, Converter={StaticResource NullToVis}}" Orientation="Vertical" Width="80" Height="30">
<TextBlock Text="{Binding Path=Name}" Margin="5,1,0,1"/>
</StackPanel>
Does anyone have any idea how this might work?
Simple case, when I want to limit GridSplitter maximum, then I can create invisible control (which only participates in layouting), adjust its Margin and use to limit grid column/row definitions:
<Grid x:Name="limiter" Margin="10,10,20,10" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding StoreWidth, Converter={local:DoubleToGridLengthConverter}, Mode=TwoWay}"
MaxWidth="{Binding ActualWidth, ElementName=gridLimit}" /> <!-- limit maximum -->
...
</Grid.ColumnDefinitions>
<GridSplitter ... />
...
</Grid>
Now complicated case: nested view with GridSplitter, which has to be limited by some layout logic of parent view.
How do I do this? I am seeking for a comfortable and reusable solution.
Currently I am doing it complicated way:
add property to parent VM to bind limiter values (e.g. ActualWidth of it);
add to nested VM property Parent;
pass parent VM instance to nested one (using property initializer);
now GridSplitter can be limited by using Parent.SplitterMaxWidth binding.
Is there any nice and MVVM-friendly approach to the problem?
For cases when GridSplitter has to be limited by parent (TBH all my layouts can be converted to ensure this) it is possible to bind to that parent and use generic converter to change value. If parent is hard to reach, then make a simple custom control:
public class GridLimiter: Grid { }
which can be used to mark that container to be easily reached by RelativeSource.FindAncestor:
<local:GridLimiter ... >
... <!-- deeply nested -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MaxWidth="{Binding ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GridLimiter}}}" />
...
</Grid.ColumnDefinitions>
<GridSplitter ... />
</Grid>
...
Moreover, making a simple markup extension converter:
public class ValueAddConverter : MarkupExtension, IValueConverter
{
public ValueAddConverter() { }
public override object ProvideValue(IServiceProvider serviceProvider) => this;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
(double)value + double.Parse((string)parameter, NumberFormatInfo.InvariantInfo);
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
allows to define offset without needs to create invisible limiter:
MaxWidth="{Binding ActualWidth, Converter={local:ValueAddConverter}, ConverterParameter=-30, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:GridLimiter}}}"
Basically, I have a list of items and sub items (all in one list). I want to indent certain specific items that are actually sub items. Is there a function or property that I can use to do this? I've tried googling for it, and even searching for it here on stack overflow - but no success.
NOTE: I'm using C# and XAML for a windows 8 store application. Not a wpf app (the documentation and code differ sometimes).
EDIT:
Here's what my XAML looks like:
<ListView
x:Name="ListView"
AutomationProperties.AutomationId="ListView"
AutomationProperties.Name="items"
TabIndex="1"
Grid.Row="1"
Margin="-10,-10,0,0"
Padding="20,0,0,45"
IsSwipeEnabled="False"
SelectionChanged="ListView_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="10,0,0,0">
<TextBlock x:Name="Item" Tag="{Binding ID}" Text="{Binding Name}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="Wrap" MaxHeight="40" FontSize="14" FontFamily="Global User Interface"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The listview is binded to an observablelist of objects.
To Expand further on another answer here... The suggested route was to add a property to the underlying class (in this case, MyClass) and bind the ItemsSource for the ListView to a list of these objects.
public class MyClass
{
public bool IsSubItem { get; set; }
}
// Elsewhere in your code, you would need a list of these object
public ObservableCollection<MyClass> MyList { get; set; }
You would also need a Converter class, which is very easy to setup:
You'd need a converter class; converters are really easy once you get the hang of them. A simple example for this scenario would be:
public class BoolToMarginConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
//Get the passed in value and convert it to a boolean - the as keyword returns a null if we can't convert to a boolean so the ?? allows us to set a default value if we get a null instead
bool isSubItem = (value as bool?) ?? false;
// If the item IS a sub item, we want a larger Left Margin
// Since the Margin Property expects a Thickness, that's what we need to return
return new Thickness(isSubItem == true ? 24 : 12, 0, 0, 0);
}
// This isn't necessary in most cases, and in this case can be ignored (just required for the Interface definition)
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Once you get this setup, you'll need to add this to your XAML as a resource:
// Add a reference to your namespace if it isn't already in your XAML
xmlns:local="using:MyNamespace"
// You'll also need to add a static resource
<Page.Resources>
<local:BoolToMarginConverter x:Key="BoolToMarginConverter" />
</Page.Resources>
// And then lastly, you'll need to update your ListView XAML to use the new information
<ListView
x:Name="ListView"
ItemsSource={Binding MyList}
<!-- Other Properties removed for space -->
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="1">
<!-- Other info removed for space -->
<StackPanel Grid.Column="0" Margin="{Binding Path=IsSubItem, Converter={StaticResource BoolToMarginConverter}}">
<!-- Other info removed for space -->
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you just want to indent the text of the sub-items you could change the margin of the text block or stack panel.
To do this you would do the following:
Create a class for the items you will be adding to the list view
Add to the class, an IsSubItem property
Create an observable collection of these items and bind them to your list view source.
In the ListView template, bind the stack panel or text block margin to the IsSubItem property using a converter to convert the IsSubItem boolean into the appropriate margin.
I have a couple of gauges in my app, and I can't figure out how to add a converter to the text binding. I read a couple of guides on msdn but I didn't manage to figure that out (I've been coding for WP8 for just a couple of weeks).
This is is a piece of the gauge:
<gauges:MarkerGaugeIndicator Value="0"
gauges:LinearGaugeRange.IndicatorOffset="35"
x:Name="GaugeBarValore"
IsAnimated="True">
<gauges:MarkerGaugeIndicator.MarkerTemplate>
<DataTemplate>
<Grid Width="73" Height="35" UseLayoutRounding="False" d:LayoutRounding="Auto" Margin="10,-2,0,0">
<TextBlock x:Name="GaugeBarPercent" Text="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
FontWeight="Thin" Margin="6,4,32,4" Width="35"/>
<Grid.RenderTransform>
<CompositeTransform Rotation="90" TranslateX="49" TranslateY="12" />
</Grid.RenderTransform>
</Grid>
</DataTemplate>
</gauges:MarkerGaugeIndicator.MarkerTemplate>
</gauges:MarkerGaugeIndicator>
The binding itself works, but I can see a lot of decimal numbers while the value moves from a round value to another.
I want to add a converter like this method:
private String double2String(double valore)
{
return Convert.ToString(Math.Round(valore)) + "%";
}
I just don't know where to put this method and how to add this as a converter inside the binding.
Thank you for your help! :)
Sergio
Create a class to hold your Converter method that implements IValueConverter interface, Example class is bellow. You have to implement method Convert and ConvertBack.
public class DoubleToString : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Math.Round((double)value).ToString() + "%";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return double.Parse(value as string);
}
}
then add the namespace to your XAML page.
xmlns:convert="clr-namespace:Your_project_name"
Next add your converter as a Resource type i to your XAML page..
<phone:PhoneApplicationPage.Resources>
<convert:DoubleToString x:Key="DoubleConvert" />
</phone:PhoneApplicationPage.Resources>
The x:Key value is the name we are going to call in our binding statement.
Then perform the data binding. I have a simple slider and a textblock with sliders value bound to the textblocks Text property
<StackPanel>
<Slider Name="slider" Maximum="100" Minimum="0" />
<TextBlock Text="{Binding Value, ElementName=slider, Converter={StaticResource DoubleConvert}}" />
</StackPanel>
Define this converter as a resource in your parent view
<UserControl.Resources>
<local:double2String x:Key="convertDouble" />
</UserControl.Resources>
And add it to the binding
<TextBlock x:Name="GaugeBarPercent" Text="{Binding, Converter={StaticResource convertDouble}}"
Don't forget to import the namespace where the converter is defined to your view
xmlns:local="clr-namespace:YOUR_NAMESPACE"
The easier way is to use StringFormat. Like this:
<Label Text="{Binding Path=SomeProperty, StringFormat='{0:F2}%' }"/>
I've created a DataTemplate for my objects in a ResourceDictionary file. The template is basically an image that is loaded from the disk. Now, what happens is that I want to align the image to a specific point on my Canvas but not by its upper left point but its center point, that's why I want to apply a translate transform for X = -Width / 2 and
Y = -Height / 2 but I don't know how to apply them via the DataTemplate.
Any help will be appreciated, thanks!
Try using databinding on Canvas' the AttachedProperties and an IValueConverter to transform the offsets to whatever you want.
For instance:
class ImageToCanvasConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return -(int)value / 2;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Two-way binding not supported
throw new InvalidOperationException();
}
}
<Grid.Resources>
<myAssembly:ImageToCanvasConverter x:Key="imageToCanvasConverter" />
<DataTemplate ...>
<Image Canvas.Left="{Binding Path=Width, Converter={StaticResource imageToCanvasConverter}, Mode=OneTime}"
Canvas.Top="{Binding Path=Height, Converter={StaticResource imageToCanvasConverter}, Mode=OneTime}"
... />
</DataTemplate>
</Grid.Resources>
you can take the advantage of using loaded event on the data template child
Example:
if you are using grid as data template content
<DataTemplate>
<Grid Loaded="Grid_Loaded">
<Image></Image>
</Grid>
</DataTemplate>
you can write the transformation code in the .cs file using sender object.