不満なのは
- UnitTestがやりづらい構造になっている
- twitterにべっとりの作りになっている
- パフォーマンスが悪い
あとは、単純に.net framework 4.0の機能をがんがん使ってみたかったり。
もちろん今の構造を徐々に修正していくのもありですが、
今ならまだ作り直した方がいいかなぁ、、と。
もちろん流用できるところはそのまま持ってくるので、
作り直しでも、そこまで今までのが無駄になるとは思っていません。
ブログの再開はあと1週間後くらいからかなぁ。。と。
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); //オプションの読込 var option = Option.Instance; //アプリケーションの実行ファイルのフォルダ var baseUri = Directory.GetParent( System.Reflection.Assembly.GetExecutingAssembly().Location).FullName; //選択されたスキン名(=フォルダ名) //まだ機能追加していないのでデフォルト限定 var skinFolderName = "Default"; if (System.IO.Directory.Exists(string.Format(@"{0}\Skins\{1}", baseUri, option.Skin))) { skinFolderName = option.Skin; } //スキンの読込 ResourceDictionary rd = new ResourceDictionary(); foreach (DictionaryEntry entry in this.Resources) {//既存のリソースに追加するため rd[entry.Key] = entry.Value; } rd.MergedDictionaries.Add( Application.LoadComponent(new Uri("/Resources/CommonTemplates.xaml", UriKind.Relative)) as ResourceDictionary); //スキンフォルダの共通スキンを読込(Skinsフォルダ直下は共通) foreach (var file in Directory.GetFiles(string.Format(@"{0}\Skins", baseUri), @"*.xaml")) { var theme = XamlReader.Load(XmlReader.Create(file)) as ResourceDictionary; rd.MergedDictionaries.Add(theme); } //選択されたスキンの読込 foreach (var file in Directory.GetFiles( string.Format(@"{0}\Skins\{1}", baseUri, skinFolderName), @"*.xaml")) { var theme = XamlReader.Load(XmlReader.Create(file)) as ResourceDictionary; rd.MergedDictionaries.Add(theme); } this.Resources = rd;
<Style x:Key="WindowStyle" TargetType="{x:Type Window}"> <Setter Property="Template" Value="{DynamicResource WindowTemplate}"/> <Setter Property="WindowStyle" Value="None"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="AllowsTransparency" Value="True"/> </Style>
<UserControl x:Class="WTwitter.View.Parts.TitlebarView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:root="clr-namespace:WTwitter" xmlns:util="clr-namespace:WTwitter.ViewModel.Utility"> <DockPanel Background="{DynamicResource TitlebarBackground}"> <!--左端のアプリのアイコン--> <Image Source="{DynamicResource AppImage}" Width="16" Height="16" DockPanel.Dock="Left"/> <!--閉じるボタン--> <Button DockPanel.Dock="Right" Command="{Binding Path=CloseCommand}" Foreground="White" Background="{DynamicResource CloseButtonBackground}" Margin="5,0,0,0" Width="25" Height="20" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"> X</Button> <!--オプション画面を開くボタン--> <Button Command="{Binding Path=OptionDialogCommand}" DockPanel.Dock="Right" Style="{DynamicResource ImageButtonStyle}" Margin="3,1"> <Image Source="{DynamicResource SettingImage}" Height="16" Width="16"/> <Button.ToolTip>設定</Button.ToolTip> </Button> <!--TwitterのHOMEをブラウザで開くボタン--> <Button Command="{x:Static util:CommonCommands.OpenByBrowser}" CommandParameter="http://twitter.com" DockPanel.Dock="Right" Style="{DynamicResource ImageButtonStyle}" Margin="3,1"> <Image Source="{DynamicResource TwitterHomeImage}" Height="16" Width="16"/> <Button.ToolTip>TwitterHomeをブラウザで開く</Button.ToolTip> </Button> <!--タイトルバーのタイトルテキスト--> <TextBlock Text="{Binding Path=DisplayName}" Foreground="White" Margin="2"/> <!--右クリックメニュー--> <DockPanel.ContextMenu> <ContextMenu> <MenuItem Header="最前面に表示(_T)" IsCheckable="True" IsChecked="{Binding Source={x:Static root:App.Current}, Path=MainWindow.Topmost, Mode=TwoWay}"/> </ContextMenu> </DockPanel.ContextMenu> </DockPanel> </UserControl>
<ControlTemplate x:Key="WindowTemplate" TargetType="{x:Type Window}"> <DockPanel Background="Transparent"> <parts:TitlebarView DockPanel.Dock="Top"/> <ContentControl Content="{TemplateBinding Property=Content}" Background="Transparent"/> </DockPanel> </ControlTemplate>
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { DragMove(); }
<ResizeGrip HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="3" Height="3" />
あとはMainWindowにResizeMode="CanResizeWithGrip"を設定しておいてください。<SolidColorBrush x:Key="HitVisibleTransparentBrush"> <SolidColorBrush.Color>White</SolidColorBrush.Color> <SolidColorBrush.Opacity>0.01</SolidColorBrush.Opacity> </SolidColorBrush> <SolidColorBrush x:Key="MouseOverBackgroundBrush"> <SolidColorBrush.Color>LightBlue</SolidColorBrush.Color> <SolidColorBrush.Opacity>0.5</SolidColorBrush.Opacity> </SolidColorBrush>
class Model : INotifyPropertyChanged { private bool _isOn; public bool IsOn { get { return _isOn; } set { if (_isOn != value) { _isOn = value; var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs("IsOn")); } } } } public event PropertyChangedEventHandler PropertyChanged; }
<Window x:Class="TriggerSample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:TriggerSample" Title="MainWindow" Height="350" Width="525"> <!--3タイプのTriggerを使ってみたサンプル--> <Window.DataContext> <local:Model x:Name="data"/> </Window.DataContext> <StackPanel> <!--自分自身のプロパティに従って、他のプロパティを変更する--> <Border BorderBrush="Green" BorderThickness="2" Margin="5"> <TextBlock>Borderの上にマウスを置いたらBorderの色が変わる</TextBlock> <Border.Style> <Style TargetType="{x:Type Border}"> <Setter Property="Background" Value="Yellow"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="Red"/> </Trigger> </Style.Triggers> </Style> </Border.Style> </Border> <!--外のプロパティに従って内側のプロパティを変更する--> <Border Background="LightBlue" BorderBrush="Red" BorderThickness="3" Name="_outer"> <StackPanel Orientation="Horizontal"> <TextBlock>外側のBorderにマウスを置いたら内側のBorderの色が変わる</TextBlock> <Border BorderBrush="Black" BorderThickness="3" CornerRadius="3"> <TextBlock>内側のBorder</TextBlock> <Border.Style> <Style TargetType="{x:Type Border}"> <Style.Triggers> <DataTrigger Binding="{Binding ElementName=_outer, Path=IsMouseOver}" Value="True"> <Setter Property="Background" Value="Blue"/> </DataTrigger> </Style.Triggers> </Style> </Border.Style> </Border> </StackPanel> </Border> <!--(Borderに関係ない)データの値に従ってプロパティを変更する--> <Border BorderBrush="Pink" BorderThickness="3" Margin="5" Name="border"> <TextBlock>データの値でBorderの色が変わる</TextBlock> <Border.Style> <Style TargetType="{x:Type Border}"> <Setter Property="Background" Value="Orange"/> <Style.Triggers> <DataTrigger Binding="{Binding ElementName=data, Path=IsOn}" Value="True"> <Setter Property="Background" Value="DarkGray"/> </DataTrigger> </Style.Triggers> </Style> </Border.Style> </Border> <CheckBox IsChecked="{Binding ElementName=data, Path=IsOn, Mode=TwoWay}">ここを押してデータを変える</CheckBox> </StackPanel> </Window>
<Button>OK</Button>
namespace WTwitter.Tests.Model.Twitter {
class TwitterItemTest {
private static class TestDataFactory {
public static Status CreateStatus() {
return new Status() {
Id = 100,
Favorited = false,
CreatedAtString = "Mon Jan 11 15:26:14 +0000 2010",
Text = "おはようございます",
InReplyToScreenName = "yuki",
InReplyToStatusId = null,
InReplyToUserId = null,
Truncated = false,
User = new User() {
Id = 33,
Following = false,
Name = "yuki_",
ScreenName = "yuki",
Url = "http://wtwitter.codeplex.com/",
ProfileImageUrl = "http://someimage",
Description = "description for user",
}
};
}
}
[Test]
public void TestConstructor() {
var status = TestDataFactory.CreateStatus();
var target = new TwitterItem(status);
Assert.AreEqual(100,target.Id);
Assert.IsFalse(target.Favorited);
var date = new DateTime(2010, 1, 11, 15, 26, 14, DateTimeKind.Utc);
Assert.AreEqual(date.ToLocalTime(), target.CreatedAt, "CreatedAtはLocalTimeで持つ");
Assert.AreEqual(ItemType.TwitterStatus, target.Type);
Assert.AreEqual("おはようございます", target.Text);
Assert.AreEqual("yuki", target.User.ScreenName);
Assert.AreEqual("yuki_", target.User.Name );
Assert.AreEqual("description for user", target.User.Description );
}
[Test]
public void TestEquality() {
//EqualsはIdで判断する
var status1 = TestDataFactory.CreateStatus();
var status2 = TestDataFactory.CreateStatus();
var status3 = TestDataFactory.CreateStatus();
status1.Id = 10;
status2.Id = 10;
status3.Id = 20;
var item1 = new TwitterItem(status1);
var item2 = new TwitterItem(status2);
var item3 = new TwitterItem(status3);
Assert.IsTrue(item1.Equals(item2));
Assert.IsFalse(item1.Equals(item3));
}
[Test]
public void TestTextComponent() {
//細かい動作はSplitterクラスのテストで確認する
var status1 = TestDataFactory.CreateStatus();
var item1 = new TwitterItem(status1);
Assert.AreEqual(1, item1.TextComponents.Count);
var status2 = TestDataFactory.CreateStatus();
status2.Text = "@yuki1090 てすと http://www.google.com てすと";
var item2 = new TwitterItem(status2);
//※"@" "yuki1090" " てすと " "http://www.google.com" " てすと"に分かれる
//IDに@は含まれないので注意
Assert.AreEqual(5, item2.TextComponents.Count);
Assert.AreEqual(TextComponentType.Plain, item2.TextComponents[0].Type);
Assert.AreEqual(TextComponentType.UserName, item2.TextComponents[1].Type);
Assert.AreEqual(TextComponentType.Plain, item2.TextComponents[2].Type);
Assert.AreEqual(TextComponentType.Url, item2.TextComponents[3].Type);
Assert.AreEqual(TextComponentType.Plain, item2.TextComponents[4].Type);
Assert.AreEqual("@", item2.TextComponents[0].Text);
Assert.AreEqual("yuki1090", item2.TextComponents[1].Text);
Assert.AreEqual(" てすと ", item2.TextComponents[2].Text);
Assert.AreEqual("http://www.google.com", item2.TextComponents[3].Text);
Assert.AreEqual(" てすと", item2.TextComponents[4].Text);
}
}
}