I am trying to pass the ListView or the GridView as a ConverterParameter
However, in the Converter routine the parameter is coming as a type string
Below is the part of the XAML List view and the Converter class.
Any help greatly appreciated. Thanks!!!
<ListView Name="SeqDtStDataListView1" Grid.Row="1"
DataContext="{Binding Path=DisplayDT[0], Converter ={StaticResource
CNVToColumn},ConverterParameter=?????}"
VerticalContentAlignment="Stretch" VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
SelectionChanged="SEQDatalistview_SelectionChanged" Margin="5">
<ListView.View >
<GridView x:Name="SeqDtStDataGridView1"/>
</ListView.View>
</ListView>
Converter:
namespace MFTest.Converters
{
public class CNVToColumn : IValueConverter
{
public object Convert(object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
DataTable dt = (DataTable)value;
GridView GV = (GridView)parameter; <========= fail here ===========
if (dt != null && GV != null)
foreach (var colum in dt.Columns) // Binding the Columns
{
DataColumn dc = (DataColumn)colum;
GridViewColumn column = new GridViewColumn();
column.DisplayMemberBinding = new Binding(dc.ColumnName);
column.Header = dc.ColumnName;
GV.Columns.Add(column);
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
From .NET 4 onwards you could use x:Reference which allows you to avoid a ElementName binding which can only be set on dependency properties while achieving pretty much the same thing.
Due to cyclical dependency restrictions you cannot reference a control inside itself or its ancestors in the tree. You can however move the binding up a level and just inherit the DataContext, e.g.
<Border DataContext="{Binding Path=DisplayDT[0],
Converter={StaticResource CNVToColumn},
ConverterParameter={x:Reference SeqDtStDataListView1}}">
<ListView Name="SeqDtStDataListView1" Grid.Row="1">
You can't bind to it.
ConvertParameter inherits from Object and therefore is not bindable.
You can, however, define your Binding in the code behind instead of doing it in the XAML part.
System.Windows.Data.Binding b = new System.Windows.Data.Binding();
b.ConverterParameter = this;
Please read about the limitations on the ConvertParameter here
You could use ConverterParameter={Binding ElementName=[insert x:Name of grid view]}
Although IMHO I'd reconsider the need - do you really need to pass a UIControl as a Converter parameter?
Related
I have ran into a issue with the converters... They are not triggering once the bound collection is updated although they trigger when the collection is first populated. I would like to have them fire whenever there is a change in the collection.
So far I have built a simple converter:
public class TableConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
VM.Measurement t = ((VM.Measurement)((TextBlock)value).DataContext);
if (t.Delta != null)
{
if (Math.Abs((double)t.Delta) < t.Tol)
return "Green";
else
return "Red";
}
else
return "Red";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
which is linked to a style
<conv:TableConverter x:Key="styleConvStr"/>
<Style x:Key="CellStyleSelectorTol" TargetType="syncfusion:GridCell">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content, Converter={StaticResource styleConvStr}}" />
</Style>
Which is used in this DataGrid
<syncfusion:SfDataGrid x:Name="CheckGrid" BorderBrush="White" Grid.Row="1" Grid.Column="1" AllowEditing="True" ItemsSource="{Binding ChecksList, Mode=TwoWay}" Background="White" SnapsToDevicePixels="False"
ColumnSizer="None" AllowResizingColumns="False" AllowTriStateSorting="True" AllowDraggingColumns="False" CurrentCellEndEdit="CheckGrid_CurrentCellEndEdit" AutoGenerateColumns="False"
NavigationMode="Cell" HeaderRowHeight="30" RowHeight="21" GridPasteOption="None" Margin="20 10 10 10" AllowGrouping="True" SelectedItem="{Binding SelectedLine, Mode=TwoWay}"
SelectionUnit="Row" SelectionMode="Single" RowSelectionBrush="#CBACCB" VirtualizingPanel.IsVirtualizing="True" Visibility="Visible">
<syncfusion:GridTextColumn Width="100" ColumnSizer="SizeToCells" AllowEditing="True" MappingName="Measured" CellStyle="{StaticResource CellStyleSelectorTol}" HeaderText="Measured" TextAlignment="Center" AllowFiltering="False" FilterBehavior="StringTyped"/>
The VM contains an Observable Collection which implements NotifyPropertyChanged all the way down to the Measurement Class. The properties fire up nicely so it is not a binding issue.
private ObservableCollection<Measurement> _checkList = new ObservableCollection<Measurement>();
public ObservableCollection<Measurement> ChecksList
{
get
{
return _checkList;
}
set
{
_checkList = value;
NotifyPropertyChanged();
}
}
Any help with this would be greatly appreciated.
Thanks
EDIT:
Here is the code that updates the collection. Apologies for it being quite messy. Lineitem is the selected line for which Measured and Delta are updated. These are properly displayed in the grid once modified.
public void NewMeasurement(VM.Measurement measurementShell)
{
using (VMEntity DB = new VMEntity())
{
var Check = CheckSets.Where(x => x.ID == SelectedLine.ID).First();
if (Check.Measurement == null)
{
Check.Measurement = measurementShell.Index;
var Lineitem = ChecksList.Where(x => x.ID == SelectedLine.ID).First();
var measurement = DB.Measurements.Where(x => x.Index == Check.Measurement).First();
Lineitem.Measured = (double)measurement.measurement1;
Lineitem.Delta = Lineitem.Measured - Lineitem.Target;
OK, it looks like the problem is that you are changing properties of the cell content item (LineItem, in the NewMeasurement() method), but it's still the same object, so the cell's content doesn't change. The cell's Content is the source for the binding. If that doesn't change, the binding won't wake up and update the target. You're raising PropertyChanged, but this particular binding has no way of knowing you want it to listen to this object for those property changes. Easy enough fix: We'll start telling it exactly what to listen for.
Fortunately the solution means simplifying some of your code. Passing a UI control into a value converter is exotic and not necessary.
What you care about in the converter is Measurement.Delta and Measurement.Tol. When either one changes, the Binding should update its target. You don't want to do that in a clever way. You just want a Binding for each one. That's a Binding's job.
So tell the Binding that you care about those properties, and rewrite the converter to accept both of them as parameters.
<Style x:Key="CellStyleSelectorTol" TargetType="syncfusion:GridCell">
<Setter
Property="Background"
>
<Setter.Value>
<MultiBinding Converter="{StaticResource styleConvStr}">
<Binding Path="Delta" />
<Binding Path="Tol" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
Converter:
public class TableConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// I'm inferring that Measurement.Delta is Nullable<double>; if that's
// not the case, change accordingly. Is it Object instead?
double? delta = (double?)values[0];
double tol = (double)values[1];
if (delta.HasValue && Math.Abs(delta.Value) < tol)
{
return "Green";
}
return "Red";
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I have a WPF App that has (so far) 2 modes of display, regularmode and widgetmode.
I am using Prism 6 with MVVM design pattern.
MainWindowViewModel knows the mode of display.
ToolBarView has, as expected, a toolbar of buttons and the buttons shall be dynamically changed to different images depending on the mode of the view. If the mode is WidgetMode, it switches to the image with an identical name but with an '_w' added. So instead of "image.png", it's "image_w.png".
What I'd like to do is create a string in ToolBarView that is updated to either String.Empty or to "_w", depending on the mode. I'd also like the image root folder to be a global string, rather than a hardcoded string, so I have defined that in app.xaml.
<Application.Resources>
<sys:String x:Key="ImageURIRoot">/MyApp;component/media/images/</sys:String>
</Application.Resources>
Then in my toolbarview (a usercontrol), I did this:
<UserControl.Resources>
<converters:StringToSourceConverter x:Key="strToSrcConvert"/>
<sys:String x:Key="BtnImgSuffix">_w</sys:String>
.
.
.
</UserControl.Resources>
Note that the string is hardcoded; eventually, I will change it dynamically based off the windowmode.
I then put the Buttons in a Listbox
<ListBoxItem Style="{StaticResource MainButton_Container}">
<Button Command="{Binding ButtonActionDelegateCommand}" Style="{StaticResource Main_Button}">
<Image Source="{Binding Source={StaticResource ImageURIRoot}, Converter={StaticResource strToSrcConvert}, ConverterParameter='{}{0}button.png'}" />
</Button>
</ListBoxItem>
Converter code:
public class StringToSourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter is string)
{
return string.Format(parameter.ToString(), value);
}
return null;
}
public object ConvertBack(object value, Type targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
So that works. But what I want is to have the ConverterParameter equal "{}{0}button{1}.png", where {0} is the URI Root and {1} is the suffix. But I can't figure out how to do it. I know it's simple, but I can't put my finger on it!
Please help!
Figured it out and it was through multibinding. The way I did it was create a converter that inherits from IMultiValueConverter. Its "Convert" method looks like this:
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
ImageSourceConverter conv = new ImageSourceConverter();
int suffixPos = ((String)parameter).Length - 4;
var returnValue = ((String)parameter).Insert(suffixPos, values[1].ToString());
returnValue = Path.Combine(values[0].ToString(), returnValue);
ImageSource imgsrc = conv.ConvertFromString(returnValue) as ImageSource;
return imgsrc;
}
The xaml looks like this:
<Image Height="30" Width="40" diag:PresentationTraceSources.TraceLevel="High">
<Image.Source>
<MultiBinding Converter="{StaticResource stringsToSrcConvert}" ConverterParameter="buttonImg.png">
<Binding Source="{StaticResource ImageURIRoot}"/>
<Binding Source="{StaticResource BtnImgSuffix}"/>
</MultiBinding>
</Image.Source>
</Image>
Also, had to modify the URIRoot
<Application.Resources>
<sys:String x:Key="ImageURIRoot">pack://application:,,,/MyApp;component/media/images/</sys:String>
</Application.Resources>
Thanks, Clemens!
my objects which I use to binding in XAML can have only string properties. But in binding I need other type. I thought that I use Converter function from IValueConverter, where I'll create object from string properties and return this. One property which is a string will be empty, and in binding I'll return other object from Converter method. I tried this but in Convert method my main object from ObservableCollection is null. This's a piece of my XAML
<Maps:MapItemsControl ItemsSource="{Binding}">
<Maps:MapItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Background="Transparent" Tapped="ItemStckPanel">
<Image Source="/Assets/pushpin.gif" Height="30" Width="30"
Maps:MapControl.Location="{Binding Location,
Converter={StaticResource StringToGeopoint}}"
Maps:MapControl.NormalizedAnchorPoint="0.5,0.5"/>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">
<TextBlock FontSize="20" Foreground="Black" Text="{Binding Name}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</Maps:MapItemsControl.ItemTemplate>
</Maps:MapItemsControl>
And this's my Convert method:
public object Convert(object value, Type targetType, object parameter, string language)
{
Event _event = (Event) parameter;
BasicGeoposition position = new BasicGeoposition();
position.Latitude = _event.Latitude;
position.Longitude = _event.Longitude;
return new Geopoint(position);
}
I want to pass the my actual parent object in Converter method. Solution is change
Maps:MapControl.Location="{Binding Location,
Converter={StaticResource StringToGeopoint}}"
to
Maps:MapControl.Location="{Binding Converter={StaticResource StringToGeopoint}}"
It works :)
The bound object is fed into the "value" parameter of the Convert()-Method.
You're accessing the parameter which corresponds to
<... ConverterParameter= .../>
which isn't set in your xaml.
You would actually have to write your Convert()-Method like this:
public object Convert(object value, Type targetType, object parameter, string language)
{
Event _event = (Event) value;
BasicGeoposition position = new BasicGeoposition();
position.Latitude = _event.Latitude;
position.Longitude = _event.Longitude;
return new Geopoint(position);
}
/UPDATE:
The ItemsSource={Binding} on your Maps:MapItemControl binds to the DataContext of the parent object. This should be your ObservableCollection.
Within the ItemTemplate your Image has a "Location"-Property that is bound to the "Location"-property of each item within your ObservableCollection. You could also write:
{Binding Path=Location, Converter={StaticResource StringToGeopoint}}
Now before that binding is fully evaluated, the Object that is stored in the Location-property is passed to the converter and the result is then handed to the "Location"-Property on the Image.
If you are getting null objects to be passed to the "value"-parameter, that means that the original Binding hands null values to the Converter either because the Property on the source object is null or because the property doesn't exist.
<ListBox x:Name="noteListBox"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" Foreground="#FF329BD6" Margin="0,24,0,85">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="noteTitle"
FontSize="40"
Text="{Binding Titolo}"
Tag="{Binding FileName}"
Foreground="#FF45B1EE" Tap="apriNota" Margin="5,0,0,0" />
<TextBlock x:Name="noteDateCreated"
Text="{Binding DateCreated}"
Margin="10,0,0,10" Foreground="#FF60ADD8" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I need to dynamically change the Foreground color of those two TextBlocks inside the StackPanel. The problem is that they don't seem to be accessible from my C# code, suppose because they're in that StackPanel.
So basically this is what I need to do:
noteTitle.Foreground = new SolidColorBrush(Color.FromArgb(255, 50, 155, 214));
But I can't even find noteTitle in my C# code... How can I fix this?
Thank you!
I would suggest using value converter for such purposes. I assume foreground value depends on a state of object whcih ahs proeprties FileName, DateCreated you bind to, so just use this object as converetr parameter and in converter do main calculation to decide which Foreground should be returned for this particular item.
public class EntityToForegroundConverter : System.Windows.Data.IValueConverter
{
public object Convert(object value,
Type targetType,
object parameter,
CultureInfo culture)
{
var entity = value as IMyEntity;
// TODO: determine a foreground value based on bound
// object proerrty values
}
public object ConvertBack(object value,
Type targetType,
object parameter,
CultureInfo culture)
{
return null;
}
}
<Control.Resources>
<src:EntityToForegroundConverter x:Key="foregroundConverter"/>
</Control.Resources>
<TextBlock
Foreground="{Binding Converter={StaticResource foregroundConverter}}" />
if you want get element you can use this code:
var a = noteListBox.ItemTemplate.LoadContent() as StackPanel;
(a as StackPanel).Background = Brushes.Red;
foreach (UIElement child in a.Children)
{
if (child is TextBlock)
if ((child as TextBlock).Name.CompareTo("noteDateCreated") == 0)
{
(child as TextBlock).Foreground = Brushes.Red;
}
}
or you want change change Template ,you can create Template by code,and overWrite Template:
DataTemplate template = new DataTemplate(typeof(Test));
FrameworkElementFactory spOuterFactory =new FrameworkElementFactory(typeof(StackPanel));
FrameworkElementFactory block1 = new FrameworkElementFactory(typeof(TextBlock));
FrameworkElementFactory block2 = new FrameworkElementFactory(typeof(TextBlock));
block1.SetValue(TextBlock.ForegroundProperty, Brushes.Red);
Binding binding1 = new Binding();
binding1.Path = new PropertyPath("Titolo");
block1.SetBinding(TextBlock.TextProperty, binding1);
block2.SetValue(TextBlock.ForegroundProperty, Brushes.Red);
Binding binding2 = new Binding();
binding2.Path = new PropertyPath("noteDateCreated");
block2.SetBinding(TextBlock.TextProperty, binding2);
spOuterFactory.AppendChild(block1);
spOuterFactory.AppendChild(block2);
template.VisualTree = spOuterFactory;
noteListBox.ItemTemplate = template;
I wish to display a text + hyperlinks in a RichTextBox from the code-behind or the binded via the Xaml if there is the possibility.
For the moment, I have a string variable with a Url (that I'd like very much to make clickable) binded to a TextBlock. I'd like to basically replace:
<TextBlock Text="{Binding myTextWithUrl}" />
by (in a richTB: )
<Run Text="partOfTextNonUrl" /><Hyperlink NavigateUri="theUrl" TargetName="whatever" />
Here is how it is presented:
I have an ItemsControl templated with a custom object
<ItemsControl ItemsSource="{Binding FeedResults}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" >
<my:SearchResultItem />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And this custom control presents the binded data in 3 TextBlocks as presented above: title, date, and the text containing text + urls.
I have already a method that extracts the urls from the string, I just don't know how to use it. I can generate dynamically Run() and Hyperlink(), and add them to the paragraph, but how to bind ?
Or any other solution ? You'd make my day!!
Thanks, Sylvain
OK. So apparently inline Hyperlinks aren't even allowed in Silverlight. But you can make your own!
http://csharperimage.jeremylikness.com/2009/11/inline-hyperlinks-in-silverlight-3.html
Not easy - at least not as easy at it should be. But it should get the job done.
Once you have the ability to add these runs with hyperlinks, the way I'd approach it is this. Create a user control with a single TextBlock (txtContent). Set the DataContext="{Binding myTextWithUrl}". Then in the code behind:
public TextWithUrlUserControl()
{
InitializeComponent();
this.Loaded += (s, e) =>
{
foreach(var inline in ParseText(DataContext as string))
txtContent.Inlines.Add(inline);
};
}
IEnumerable<Inline> ParseText(string text)
{
// return list of Runs and Runs with hyperlinks using your URL parsing
// for demo purposes, just hardcoding it here:
return new List<Inline>
{
new Run{Text="This text has a "},
new Run{Text="URL", RunExtender.NavigateUrl="http://www.google.com/"},
new Run{Text="in it!"}
};
}
Hope this is helpful.
I would do something like this. Create a ValueConverter which will take your text (with the URL in it). Then in your TextBlock, create the Run and Hyperlink - bind both to the text, both using the ValueConverter, but with a different parameter to the ValueConverter.
The ValueConverter:
public class MyCustomValueConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if(parameter.ToString()== "URL")
{
// return the URL part of the string
}
else
{
// return the non-URL portion of the string
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then your XAML looks like this:
<Run Text="{Binding myTextWithUrl, Converter={StaticResource valueConverter}}"></Run><Hyperlink NavigateUri="{Binding myTextWithUrl, Converter={StaticResource valueConverter}, ConverterParameter=URL}"></Hyperlink>