|
You could have said that at the outset: "I will only entertain MVVM solutions". Noted.
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
Sorry for the confusion. I just asumed that MVVM is the way most folks do things.
So I found out a way to load my control:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Load();
}
Not sure if this is the best way, but it works.
Thanks
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
I'm having trouble using Path data for an image. I followed the accepted answer here, but I'm still having issues.
<Button Height="50"
Width="50">
<Viewbox Stretch="Uniform">
<Canvas Height="122.88"
Width="122.88">
<Path Fill="Red"
Data="M73.48,15.84A46.87,46.87,0,0,1,84.87,21L91,14.84a7.6,7.6,0,0,1,10.72,0L108,21.15a7.6,7.6,0,0,1,0,10.72l-6.6,6.6a46.6,46.6,0,0,1,4.34,10.93h9.52A7.6,7.6,0,0,1,122.88,57V65.9a7.6,7.6,0,0,1-7.58,7.58h-9.61a46.83,46.83,0,0,1-4.37,10.81L108,91a7.6,7.6,0,0,1,0,10.72L101.73,108A7.61,7.61,0,0,1,91,108l-6.34-6.35a47.22,47.22,0,0,1-11.19,5v8.59a7.6,7.6,0,0,1-7.58,7.58H57a7.6,7.6,0,0,1-7.58-7.58v-7.76a47.39,47.39,0,0,1-12.35-4.68L31.87,108a7.62,7.62,0,0,1-10.72,0l-6.31-6.31a7.61,7.61,0,0,1,0-10.72l4.72-4.72A47.38,47.38,0,0,1,14,73.48H7.58A7.6,7.6,0,0,1,0,65.9V57A7.6,7.6,0,0,1,7.58,49.4h6.35a47.2,47.2,0,0,1,5.51-12.94l-4.6-4.59a7.62,7.62,0,0,1,0-10.72l6.31-6.31a7.6,7.6,0,0,1,10.72,0l5,5A46.6,46.6,0,0,1,49.4,15V7.58A7.6,7.6,0,0,1,57,0H65.9a7.6,7.6,0,0,1,7.58,7.58v8.26ZM59.86,36.68a24.6,24.6,0,1,1-24.6,24.59,24.59,24.59,0,0,1,24.6-24.59Z" />
</Canvas>
</Viewbox>
</Button>
If I change the size of the Canvas, the image skews off to the side.
How can I get the Gear icon to be a fixed size and stay centered regardless of the button or canvas size?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
If you want to "fix the size" of the path figure, set the Width and Height on the Viewbox (otherwise it stretches or shrinks).
You could probably do away with Canvas alltogether in this case (coordinates can operate in "virtual space").
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
OK, so here's what I have now:
<Button Height="25"
Width="75">
<Viewbox Stretch="Uniform"
Height="20"
Width="20">
<Canvas Height="122.88"
Width="122.88">
<Path Fill="Red"
Data="M73.48,15.84A46.87,46.87,0,0,1,84.87,21L91,14.84a7.6,7.6,0,0,1,10.72,0L108,21.15a7.6,7.6,0,0,1,0,10.72l-6.6,6.6a46.6,46.6,0,0,1,4.34,10.93h9.52A7.6,7.6,0,0,1,122.88,57V65.9a7.6,7.6,0,0,1-7.58,7.58h-9.61a46.83,46.83,0,0,1-4.37,10.81L108,91a7.6,7.6,0,0,1,0,10.72L101.73,108A7.61,7.61,0,0,1,91,108l-6.34-6.35a47.22,47.22,0,0,1-11.19,5v8.59a7.6,7.6,0,0,1-7.58,7.58H57a7.6,7.6,0,0,1-7.58-7.58v-7.76a47.39,47.39,0,0,1-12.35-4.68L31.87,108a7.62,7.62,0,0,1-10.72,0l-6.31-6.31a7.61,7.61,0,0,1,0-10.72l4.72-4.72A47.38,47.38,0,0,1,14,73.48H7.58A7.6,7.6,0,0,1,0,65.9V57A7.6,7.6,0,0,1,7.58,49.4h6.35a47.2,47.2,0,0,1,5.51-12.94l-4.6-4.59a7.62,7.62,0,0,1,0-10.72l6.31-6.31a7.6,7.6,0,0,1,10.72,0l5,5A46.6,46.6,0,0,1,49.4,15V7.58A7.6,7.6,0,0,1,57,0H65.9a7.6,7.6,0,0,1,7.58,7.58v8.26ZM59.86,36.68a24.6,24.6,0,1,1-24.6,24.59,24.59,24.59,0,0,1,24.6-24.59Z" />
</Canvas>
</Viewbox>
</Button>
The Gear image now is halfway hidden below.
Here's what it looks like now
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
No, I'm not seeing that. You're either inheriting something; or it's a version thing. I tested it with a new WPF .NET 6.0 project.
<Window x:Class="WpfApp1.MainWindow"
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:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<Button Height="25"
Width="75">
<pre>
<Viewbox Stretch="Uniform"
Height="20"
Width="20">
<Canvas Height="122.88"
Width="122.88">
<Path Fill="Red"
Data="M73.48,15.84A46.87,46.87,0,0,1,84.87,21L91,14.84a7.6,7.6,0,0,1,10.72,0L108,21.15a7.6,7.6,0,0,1,0,10.72l-6.6,6.6a46.6,46.6,0,0,1,4.34,10.93h9.52A7.6,7.6,0,0,1,122.88,57V65.9a7.6,7.6,0,0,1-7.58,7.58h-9.61a46.83,46.83,0,0,1-4.37,10.81L108,91a7.6,7.6,0,0,1,0,10.72L101.73,108A7.61,7.61,0,0,1,91,108l-6.34-6.35a47.22,47.22,0,0,1-11.19,5v8.59a7.6,7.6,0,0,1-7.58,7.58H57a7.6,7.6,0,0,1-7.58-7.58v-7.76a47.39,47.39,0,0,1-12.35-4.68L31.87,108a7.62,7.62,0,0,1-10.72,0l-6.31-6.31a7.61,7.61,0,0,1,0-10.72l4.72-4.72A47.38,47.38,0,0,1,14,73.48H7.58A7.6,7.6,0,0,1,0,65.9V57A7.6,7.6,0,0,1,7.58,49.4h6.35a47.2,47.2,0,0,1,5.51-12.94l-4.6-4.59a7.62,7.62,0,0,1,0-10.72l6.31-6.31a7.6,7.6,0,0,1,10.72,0l5,5A46.6,46.6,0,0,1,49.4,15V7.58A7.6,7.6,0,0,1,57,0H65.9a7.6,7.6,0,0,1,7.58,7.58v8.26ZM59.86,36.68a24.6,24.6,0,1,1-24.6,24.59,24.59,24.59,0,0,1,24.6-24.59Z" />
</Canvas>
</Viewbox>
</Button>
</Grid>
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
In Visual Studio(Community Edition, latest version), when I select an image for the background on a page in WPF, the image file is being copied from the images folder to where the page file is. Looked around and can't find a way to turn this off.
Is there a way to turn this off, and why is Visual Studio doing this?
Thanks
[Tim]
|
|
|
|
|
tbenner1960 wrote: when I select an image for the background on a page in WPF
How did you do this, exactly?
tbenner1960 wrote: Is there a way to turn this off, and why is Visual Studio doing this?
I'm wondering how you got VS to do this because it's not a feature that can be turned on and off, it's not even a feature!, and I can't duplicate the problem on my machine.
|
|
|
|
|
Dave
To be more specific, I'm adding the image to the Grid. In the properties window I select a tile brush for the background. The image is in an images folder under the project. What happens then is the selected image is copied from the images folder to the folder where the page is. Also the link to the image points to the copied image, not the file in the images folder.
Yeah strange; these things happen to me.
[Tim]
modified 17-Dec-22 9:45am.
|
|
|
|
|
That explains it. You have MUCH greater control just by typing the XAML directly. Nobody uses the Properties window to do this, or anything else for that matter.
For example:
<DataGrid>
<DataGrid.Background>
<ImageBrush TileMode="Tile"
ImageSource="Images\Tile.bmp"
Viewport="0,0,.2,.4"
ViewportUnits="RelativeToBoundingBox" />
</DataGrid.Background>
</DataGrid>
|
|
|
|
|
I'm trying (or want) to to customize the TabPanel section of the TabControl.
The TabPanel contains the TabItem. (I can customize the TabItem)
I'd like to be able to add a button at the right side of the TabPanel, something like :
|Tab1|Tab2|Tab3 ............... Button|
|-------------------------------------|
| Tab Content |
Is this possible ?
I assume I have to go knee deep in the TabPanel Template or something like that ?
Any suggestions ?
Thanks.
Simple solution:
I found a simple solution, I put the TabControl and the Button on different ZIndex.
It kinda work. maybe will be enough.
CI/CD = Continuous Impediment/Continuous Despair
modified 15-Dec-22 10:47am.
|
|
|
|
|
I'm setting up a sample .Net Core WPF app. I want to implement Unit Testing and I'd like to learn the right way to use DI with Views & View Models.
So far I have this
public partial class App : Application
{
public static IHost? AppHost { get; private set; }
public App()
{
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton<LoginViewModel>();
services.AddSingleton<LoginView>();
})
.Build();
}
protected override void OnStartup(StartupEventArgs e)
{
AppHost!.Start();
base.OnStartup(e);
}
protected override async void OnExit(ExitEventArgs e)
{
await AppHost!.StopAsync();
base.OnExit(e);
}
}
How does the LoginView get the LoginViewModel? In my .Net Framework apps I use a ViewModel locator. Is there a best practice with WPF .Net Core, or would that work fine here also?
Thanks
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Watch this: Models - WPF MVVM TUTORIAL #1 - YouTube[^] - he will answer many questions for you, including this one!
Graeme
"I fear not the man who has practiced ten thousand kicks one time, but I fear the man that has practiced one kick ten thousand times!" - Bruce Lee
|
|
|
|
|
(Again, another complete newbie).
I'm trying to bind an ItemSource to a ListView.
From what I understand, the data should be accessible via a Property.
The main Model is "MyModel" it contains a list of items. (eventually will contain lot more data).
I'm not sure what incantation I'm missing.
Thanks.
public class Item
{
public string Name { get; set; }
public Item(string name)
{
Name=name;
}
}
public class ItemsList
{
public List<Item> Items
{
get; private set;
}
public ItemsList()
{
Items = new List<Item>
{
new Item("a"),
new Item("b"),
new Item("c")
};
}
}
public class MyModel
{
private readonly ItemsList _list;
public List<Item> Items => _list.Items;
public MyModel()
{
_list = new ItemsList();
}
}
App.xaml.ca :
protected override void OnStartup(StartupEventArgs e)
{
MainWindow = new MainWindow()
{
DataContext = new MyModel()
};
MainWindow.Show();
base.OnStartup(e);
}
MainWindow :
<Grid>
<!-- here ... pass the datacontext to my component. -->
<components:MyListView DataContext="MyModel"/>
</Grid>
MyListView.xaml component. :
<Grid>
<!-- here ... what binding source should I put in ? -->
<ListView Margin="10" ItemsSource="{Binding Items}"/>
</Grid>
CI/CD = Continuous Impediment/Continuous Despair
modified 8-Dec-22 10:23am.
|
|
|
|
|
Maximilien wrote:
<components:MyListView DataContext="MyModel"/> That line's wrong for a start. The DataContext has already been set for the window, and will be inherited by the MyListView control.
Maximilien wrote:
<ListView Margin="10" ItemsSource="{Binding Items}"/> That looks correct. Are you getting any binding errors in the output window?
XAML data binding diagnostics - Visual Studio (Windows) | Microsoft Learn[^]
NB: Your view-models should really implement INotifyPropertyChanged[^], and the collection should be an ObservableCollection<T>[^], so that the binding system can pick up any changes. But at the moment you don't seem to be making any changes, so that's unlikely to be the issue.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
|
I'm writing a WPF (C#) app for my MP3 collection with more then 350,000 files.
I'm reading path and filenames directly from the HD and store the infos in a SQLite Database.
Because of reorganizationes I have to do that in periodic intervals.
But the current code needs 17 hours (!!!) for that. I'm sure there must be a faster way.
Does anyone have an idea?
private void ButtonImport_Click(object sender, RoutedEventArgs e)
{
string initdir = @"H:\mp3\";
Cursor cu = this.Cursor;
this.Cursor = Cursors.Wait;
string errtxt = "";
ToolStripStatusLabel1.Text = "deleting ..."
try
{
SQLiteCommand dbCom = new SQLiteCommand("DELETE FROM Dateien", dbConn);
dbCom.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "delete error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
sPfad = initdir;
DateTime t1 = DateTime.Now;
int z = 0;
loopDir(sPfad);
DateTime t2 = DateTime.Now;
txt = "";
txt += z.ToString() + " files saved in ";
txt += ((t2 - t1).TotalSeconds / 60).ToString("0.0") + " min.";
txt += errtxt != "" ? Environment.NewLine + Environment.NewLine + errtxt : "";
this.Cursor = cu;
MessageBox.Show(txt, "Import", MessageBoxButton.OK, MessageBoxImage.Information);
}
private void loopDir(string strPath)
{
string dn = "";
DirectoryInfo ofs = new DirectoryInfo(strPath);
bool f = false;
int n = 0;
try
{
n = ofs.GetDirectories().Length;
}
catch (Exception ex)
{
f = true;
errtxt += ex.Message + Environment.NewLine;
}
if (f == false)
{
foreach (DirectoryInfo d in ofs.GetDirectories())
{
dn = strPath + "\\" + d.Name;
dn = dn.Replace("\\\\", "\\");
loopDir(dn);
}
bool ok = false;
foreach (FileInfo fl in ofs.GetFiles())
{
ok = false;
for (int i = 0; i < fext.Length; i++)
{
if (fl.Name.ToLower().EndsWith(fext[i]))
ok = true;
}
if (ok == true)
{
string sqls = "";
sqls += "INSERT INTO Dateien (Pfad, Datei) VALUES (";
sqls += "'" + sqlready(strPath).ToString().Substring(2) + "\\', ";
sqls += "'" + sqlready(fl.Name) + "')";
try
{
SQLiteCommand dbCom = new SQLiteCommand(sqls, dbConn);
dbCom.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "database save error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
z++;
}
}
}
}
Thanks
|
|
|
|
|
I found an answer myself:
Reading all 350,000 files from the HD takes 6.2 minutes (!!!).
So the bottleneck is SQLite and I think I have to use SQL transactions to speed up my app.
|
|
|
|
|
Transactions do not speed up database operations. Transactions encapsulate multiple operations so they execute as a single set. If any operation fails, all of the operations in the transaction are rolled back.
What you should have been doing is using a parameterized INSERT query, and NOT that string concatenation crap.
|
|
|
|
|
Well, that code is ... interesting. There's a lot of needless work going on there.
Try something like this instead:
private void loopDir(DirectoryInfo folder, DataTable table, List<string> errors)
{
try
{
foreach (DirectoryInfo subFolder in folder.EnumerateDirectories())
{
loopDir(subFolder, table, errors);
}
string pfad = folder.FullName.Substring(2);
foreach (FileInfo file in folder.EnumerateFiles())
{
string extension = Path.GetExtension(file.Name);
if (!fext.Contains(extension)) continue;
table.Rows.Add(pfad, file.Name);
}
}
catch (Exception ex)
{
errors.Add(ex.Message);
}
}
private int ImportFiles(string initialFolder, List<string> errors)
{
DirectoryInfo folder = new DirectoryInfo(initialFolder);
DataTable table = new DataTable();
table.Columns.Add("Pfad", typeof(string));
table.Columns.Add("Datei", typeof(string));
loopDir(folder, table, errors);
if (table.Rows.Count == 0)
{
MessageBox.Show("No files found to import.", "Import", MessageBoxButton.OK, MessageBoxImage.Error);
return 0;
}
using (SQLiteTransaction dbTrans = dbConn.BeginTransaction())
{
using (SQLiteCommand dbCom = new SQLiteCommand("DELETE FROM Dateien", dbConn, dbTrans))
{
dbCom.ExecuteNonQuery();
}
using (SQLiteCommand dbCom = new SQLiteCommand("INSERT INTO Dateien (Pfad, Datei) VALUES ($Pfad, $Datei)", dbConn, dbTrans))
{
SQLiteParameter pPfad = dbCom.CreateParameter();
pPfad.ParameterName = "$Pfad";
dbCom.Parameters.Add(pPfad);
SQLiteParameter pDatei = dbCom.CreateParameter();
pDatei.ParameterName = "$Datei";
dbCom.Parameters.Add(pDatei);
foreach (DataRow row in table.Rows)
{
pPfad.Value = row["Pfad"];
pDatei.Value = row["Datei"];
dbCom.ExecuteNonQuery();
}
}
transaction.Commit();
}
return table.Rows.Count;
}
private void ButtonImport_Click(object sender, RoutedEventArgs e)
{
Cursor cu = this.Cursor;
this.Cursor = Cursors.Wait;
try
{
const string initdir = @"H:\mp3\";
List<string> errors = new List<string>();
Stopwatch sw = Stopwatch.StartNew();
int recordCount = ImportFiles(initdir);
sw.Stop();
string message = $"{recordCount} files saved in {sw.Elapsed.TotalMinutes:0.0} minutes.";
if (errors.Count != 0) message += Environment.NewLine + Environment.NewLine + string.Join(Environment.NewLine, errors);
MessageBox.Show(message, "Import", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Import Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
this.Cursor = cu;
}
}
NB: SQLite doesn't have a "bulk insert" option, so you need to use a transaction and a single SQLiteCommand to perform the insert:
Bulk insert - Microsoft.Data.Sqlite | Microsoft Learn[^]
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
modified 7-Dec-22 4:38am.
|
|
|
|
|
Thanks Richard, thats really great.
A little correction to the following line:
using (SQLiteCommand dbCom = new SQLiteCommand("INSERT INTO Dateien (Pfad, Datei) VALUES ($Pfad, $Datei)"))
to
using (SQLiteCommand dbCom = new SQLiteCommand("INSERT INTO Dateien (Pfad, Datei) VALUES ($Pfad, $Datei)", dbConn, dbTrans))
And now it works perfectly.
And wow, it's done in 12 seconds !!! Very impressive !!!
THANKS!
|
|
|
|
|
I have created a Hyperlink control. What I want is to have a default style in my control itself, but allow consumers to restyle it later, like when It's dropped into a window.
So here's my control's XAML:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Marois.Framework.Core.WPF.Controls"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors">
<SolidColorBrush x:Key="hyperlinkMouseOverBrush" Color="Green"/>
<Style TargetType="{x:Type local:MaroisHyperlink}">
<Setter Property="FontSize" Value="60" />
<Setter Property="Hyperlink.TextDecorations" Value="None" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBlock>
<Hyperlink>
<TextBlock Text="{Binding LinkText, RelativeSource={RelativeSource TemplatedParent}}"
FontSize="{TemplateBinding FontSize}"/>
</Hyperlink>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Hyperlink.Foreground" Value="{StaticResource hyperlinkMouseOverBrush}" />
<Setter Property="Hyperlink.TextDecorations" Value="Underline" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Hyperlink.Foreground" Value="Gray" />
<Setter Property="Hyperlink.TextDecorations" Value="None" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
Code behind:
using System.Windows;
using System.Windows.Media;
namespace Marois.Framework.Core.WPF.Controls
{
public class MaroisHyperlink : ControlBase
{
#region DP's
#region DP HoverBrush
public static readonly DependencyProperty HoverBrushProperty =
DependencyProperty.Register("HoverBrush",
typeof(SolidColorBrush),
typeof(MaroisHyperlink),
new PropertyMetadata(new SolidColorBrush(Colors.Black)));
public SolidColorBrush HoverBrush
{
get { return (SolidColorBrush)GetValue(HoverBrushProperty); }
set { SetValue(HoverBrushProperty, value); }
}
#endregion
#region DP LinkText
public static readonly DependencyProperty LinkTextProperty =
DependencyProperty.Register("LinkText",
typeof(string),
typeof(MaroisHyperlink),
new PropertyMetadata("MaroisHyperlink"));
public string LinkText
{
get { return (string)GetValue(LinkTextProperty); }
set { SetValue(LinkTextProperty, value); }
}
#endregion
#endregion
#region CTOR
static MaroisHyperlink()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MaroisHyperlink),
new FrameworkPropertyMetadata(typeof(MaroisHyperlink)));
}
#endregion
}
}
Some of this works:
- LinkText DP works. I can set it's default value and that shows up when I put the control on the window. Changing it in the window works also
- The FontSize setter at the top works.
- The Hyperlink.TextDecorations does not.
- None of the triggers at the bottom work.
In the Window
<Window x:Class="Marois.Framework.Core.WPF.Controls.Demo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:marois="clr-namespace:Marois.Framework.Core.WPF.Controls;assembly=Marois.Framework.Core.WPF.Controls"
mc:Ignorable="d"
WindowStartupLocation="CenterScreen"
Title="MainWindow"
Height="450"
Width="800">
<Window.Resources>
<Style TargetType="{x:Type marois:MaroisHyperlink}">
<Setter Property="Foreground" Value="DarkTurquoise"/>
<Setter Property="HoverBrush" Value="Brown"/>
<Setter Property="FontSize" Value="18"/>
</Style>
</Window.Resources>
<Grid>
<marois:MaroisHyperlink Grid.Row="0"
Grid.Column="0"
LinkText="Click me!"
Margin="5">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LinkClicked">
<i:InvokeCommandAction Command="{Binding MaroisLinkClickedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</marois:MaroisHyperlink>
</Grid>
</Window>
In the style
- The FontSize works
- The HoverBrush does not work
- The Foreground does not work
So basically I can style the FontSize, but nothing else. I'm not really sure how to make this all work. I'd like it to be like any other 3rd party control - Have default values that you can restyle.
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Kevin Marois wrote: The Hyperlink.TextDecorations does not.
Firstly, it's Inline.TextDecorations , not Hyperlink.TextDecorations .
Inline.TextDecorations Property (System.Windows.Documents) | Microsoft Learn[^]
Setting that property on a parent element doesn't affect the Hyperlink , since it overrides the property in its default template. You either need to set the property explicitly on the Hyperlink :
<TextBlock>
<Hyperlink TextDecorations="None"> or via a style:
<TextBlock>
<Style TargetType="Hyperlink">
<Setter Property="TextDecorations" Value="None" />
</Style>
<Hyperlink> If you want to be able to override it when you use your control, you'll need a property to control it:
public class MaroisHyperlink : ControlBase
{
public static readonly DependencyProperty TextDecorationsProperty = Inline.TextDecorationsProperty.AddOwner(typeof(MaroisHyperlink));
public TextDecorationCollection TextDecorations
{
get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); }
set { SetValue(TextDecorationsProperty, value); }
} and then bind the property in your template:
<Hyperlink TextDecorations="{TemplateBinding TextDecorations}">
Kevin Marois wrote: None of the triggers at the bottom work.
A similar problem. The triggers should set the properties of your control, and the relevant properties of the Hyperlink should be bound to the properties on your control.
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="{TemplateBinding HoverBrush}" />
<Setter Property="TextDecorations" Value="Underline" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="Gray" />
<Setter Property="TextDecorations" Value="None" />
</Trigger>
</Style.Triggers>
<Hyperlink TextDecorations="{TemplateBinding TextDecorations}" Foreground="{TemplateBinding Foreground}>
Kevin Marois wrote: The HoverBrush does not work
The HoverBrush property isn't used anywhere in your template. Changing the triggers as per the previous paragraph should resolve that, as well as the foreground brush styling.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thanks. I learned some new things here.
One question -
That DP syntax you used is unfamiliar to me. How do you set the default value for the TextDecoration's DP?
I've always used this syntax for a DP, but for some reason it won't compile here:
public static readonly DependencyProperty TextDecorationsProperty =
DependencyProperty.Register("TextDecorations",
typeof(TextDecorations),
typeof(MaroisHyperlink),
new PropertyMetadata(TextDecorations.Underline));
public TextDecorations TextDecorations
{
get { return (TextDecorations)GetValue(TextDecorationsProperty);}
set { SetValue(TextDecorationsProperty, value); }
}
I get 3 compilation errors:
Cannot convert to static type 'TextDecorations'
'TextDecorations': static types cannot be used as return types
'TextDecorations': static types cannot be used as paramters
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
|